else 策略模式去掉if_使用策略模式优化过多的if else语句

此处的过多是指if else超过三层,如下面的代码:public class MainStart {

public static void main(String[] args) {

String message = "select";

if(message.equals("select")){

System.out.println("select command!");

}else if (message.equals("abort")){

System.out.println("abort command!");

}else if (message.equals("add")){

System.out.println("add command!");

}else{

System.out.println("no command!");

}

}

}

在上面的代码中,main方法根据message的类型处理不同的逻辑,此处if else使用过度,可以使用策略模式进行优化。

策略模式是一种解耦的方法,它对算法进行封装,使得算法的调用和算法本身分离。使用策略模式客户端代码不需要调整,算法之间可以互相替换,因为不同的算法实现的是同一个接口。将上面的代码优化后变为:public class MainStart {

public static void main(String[] args) {

String message = "select";

CommandContext commandContext = new CommandContext();

CommandStrategy commandStrategy = commandContext.getInstance(message);

commandStrategy.process(message);

}

}

上面的commandContext.getInstance(message)根据message类型反射获取对象实例,再调用process(message)处理逻辑。

实现策略模式需要以下几个步骤:

1.定义接口public interface CommandStrategy {

void process(String message);

}

2.实现接口,重写处理逻辑public class AbortCommand implements CommandStrategy {

@Override

public void process(String message) {

System.out.println("command type:"+message);

}

}public class AddCommand implements CommandStrategy {

@Override

public void process(String message) {

System.out.println("command type:"+message);

}

}public class SelectCommand implements CommandStrategy {

@Override

public void process(String message) {

System.out.println("command type:"+message);

}

}

3.定义策略上下文,根据message类型获取对象实例public class CommandContext {

public CommandStrategy getInstance(String commandType) {

CommandStrategy commandStrategy = null;

Map allClazz = CommandEnum.getAllClazz();

String clazz = allClazz.get(commandType.trim().toLowerCase());

if (StringUtils.isNoneEmpty(clazz)) {

try {

try {

commandStrategy = (CommandStrategy) Class.forName(clazz).newInstance();//调用无参构造器创建实例

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

return commandStrategy;

}

}

在这一步骤中,我们需要一种方式可以根据message类型来反射获取对象的实例,这里使用枚举来维护二者的对应关系。public enum CommandEnum {

SELECT("select", "com.jxz.java.demo.command.SelectCommand"), ADD("add", "com.jxz.java.demo.command.AddCommand"), ABORTED("abort", "com.jxz.java.demo.command.AbortCommand");

private String command;

private String clazz;

public static Map getAllClazz() {

Map map = new HashMap<>(8);

for (CommandEnum commandEnum : CommandEnum.values()) {

map.put(commandEnum.getCommand(), commandEnum.getClazz());

}

return map;

}

public String getCommand() {

return command;

}

CommandEnum(String command, String clazz) {

this.command = command;

this.clazz = clazz;

}

public void setCommand(String command) {

this.command = command;

}

public String getClazz() {

return clazz;

}

public void setClazz(String clazz) {

this.clazz = clazz;

}

}

在上面的代码中,getAllClazz()方法用于获取所有message和对应处理类的映射关系。至此策略模式优化就已经完成了,运行MainStart可以看到运行结果。

但是这里还有一个不够优雅的地方,就是message类型和处理类的映射关系还是需要手动维护,不够智能,如果可以自动识别就比较方便了,如果新增业务逻辑,只需要再增加一个类就可以了。

因为参考了spring自动扫描包下类的方式,在此添加spring依赖:

org.springframework

spring-core

5.1.5.RELEASE

org.springframework

spring-context

5.1.5.RELEASE

工程目录结构如下:

164150015_1_20190621045040597_wm

util包中的ClassResourcePatternResolver类是查找command包中的所有类,此处实现了spring的资源接口ResourceLoaderAware,代码如下:package com.jxz.java.demo.util;

import org.springframework.context.ResourceLoaderAware;

import org.springframework.core.io.Resource;

import org.springframework.core.io.ResourceLoader;

import org.springframework.core.io.support.ResourcePatternResolver;

import org.springframework.core.io.support.ResourcePatternUtils;

import org.springframework.core.type.classreading.CachingMetadataReaderFactory;

import org.springframework.core.type.classreading.MetadataReader;

import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.io.IOException;

import java.net.URL;

import java.util.LinkedHashSet;

import java.util.Set;

/**

* @Author JiangXiaoZhi

* @Date 2019/3/18 10:42

* @ModifyDate 2019/3/18 10:42

* @Version 1.0

*/

public class ClassResourcePatternResolver implements ResourceLoaderAware {

private static ResourceLoader resourceLoader;

public static Set findAllClassPathResources() throws IOException {

ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);

MetadataReaderFactory metaReader = new CachingMetadataReaderFactory(resourceLoader);

// 通过classpath

// Resource[] resources = resolver.getResources("classpath*:com/jxz/java/demo/command/*.class");

// 通过file

// Resource[] resources = resolver.getResources("file:/F:/flink-demo/target/classes/com/jxz/java/demo/command/*.class");

String classResourcesPath = getClassResourcesPath();

Resource[] resources = resolver.getResources(classResourcesPath);

Set set = new LinkedHashSet<>(16);

for (Resource r : resources) {

MetadataReader reader = metaReader.getMetadataReader(r);

String className = reader.getClassMetadata().getClassName();

set.add(className);

}

return set;

}

@Override

public void setResourceLoader(ResourceLoader resourceLoader) {

this.resourceLoader = resourceLoader;

}

// 获取上一层command包目录

private static String getClassResourcesPath() {

URL url = ClassResourcePatternResolver.class.getResource("../");

String protocol = url.getProtocol();

String path = url.getPath();

return protocol + ":" + path + "command/*.class";

}

}DynamicEnumUtil类动态添加枚举类型,package com.jxz.java.demo.util;

import sun.reflect.ConstructorAccessor;

import sun.reflect.FieldAccessor;

import sun.reflect.ReflectionFactory;

import java.lang.reflect.AccessibleObject;

import java.lang.reflect.Array;

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

/**

* @Author JiangXiaoZhi

* @Date 2019/3/18 18:21

* @ModifyDate 2019/3/18 18:21

* @Version 1.0

*/

public class DynamicEnumUtil {

private static ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();

private static void setFailsafeFieldValue(Field field, Object target, Object value) throws NoSuchFieldException,

IllegalAccessException {

// let's make the field accessible

field.setAccessible(true);

// next we change the modifier in the Field instance to

// not be final anymore, thus tricking reflection into

// letting us modify the static final field

Field modifiersField = Field.class.getDeclaredField("modifiers");

modifiersField.setAccessible(true);

int modifiers = modifiersField.getInt(field);

// blank out the final bit in the modifiers int

modifiers &= ~Modifier.FINAL;

modifiersField.setInt(field, modifiers);

FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);

fa.set(target, value);

}

private static void blankField(Class> enumClass, String fieldName) throws NoSuchFieldException,

IllegalAccessException {

for (Field field : Class.class.getDeclaredFields()) {

if (field.getName().contains(fieldName)) {

AccessibleObject.setAccessible(new Field[]{field}, true);

setFailsafeFieldValue(field, enumClass, null);

break;

}

}

}

private static void cleanEnumCache(Class> enumClass) throws NoSuchFieldException, IllegalAccessException {

blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6

blankField(enumClass, "enumConstants"); // IBM JDK

}

private static ConstructorAccessor getConstructorAccessor(Class> enumClass, Class>[] additionalParameterTypes)

throws NoSuchMethodException {

Class>[] parameterTypes = new Class[additionalParameterTypes.length + 2];

parameterTypes[0] = String.class;

parameterTypes[1] = int.class;

System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);

return reflectionFactory.newConstructorAccessor(enumClass.getDeclaredConstructor(parameterTypes));

}

private static Object makeEnum(Class> enumClass, String value, int ordinal, Class>[] additionalTypes,

Object[] additionalValues) throws Exception {

Object[] parms = new Object[additionalValues.length + 2];

parms[0] = value;

parms[1] = Integer.valueOf(ordinal);

System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);

return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));

}

/**

* Add an enum instance to the enum class given as argument

*

* @param the type of the enum (implicit)

* @param enumType the class of the enum to be modified

* @param enumName the name of the new enum instance to be added to the class.

*/

@SuppressWarnings("unchecked")

public static > void addEnum(Class enumType, String enumName, Class>[] additionalTypes, Object[] additionalValues) {

// 0. Sanity checks

if (!Enum.class.isAssignableFrom(enumType)) {

throw new RuntimeException("class " + enumType + " is not an instance of Enum");

}

// 1. Lookup "$VALUES" holder in enum class and get previous enum instances

Field valuesField = null;

Field[] fields = enumType.getDeclaredFields();

for (Field field : fields) {

if (field.getName().contains("$VALUES")) {

valuesField = field;

break;

}

}

AccessibleObject.setAccessible(new Field[]{valuesField}, true);

try {

// 2. Copy it

T[] previousValues = (T[]) valuesField.get(enumType);

List values = new ArrayList(Arrays.asList(previousValues));

// 3. build new enum

T newValue = (T) makeEnum(enumType, enumName, values.size(), additionalTypes, additionalValues);

// 4. add new value

values.add(newValue);

// 5. Set new values field

setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0)));

// 6. Clean enum cache

cleanEnumCache(enumType);

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException(e.getMessage(), e);

}

}

}CommandEnum类稍作修改,先是动态添加了枚举类型,没有直接返回map类型,这样做是保证了类型安全。完整代码如下:package com.jxz.java.demo.strategy;

import com.jxz.java.demo.util.ClassResourcePatternResolver;

import com.jxz.java.demo.util.DynamicEnumUtil;

import java.io.IOException;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

/**

* @Author JiangXiaoZhi

* @Date 2019/3/13 14:31

* @ModifyDate 2019/3/13 14:31

* @Version 1.0

*/

public enum CommandEnum {;

//SELECT("select", "com.jxz.java.demo.command.SelectCommand"), ADD("add", "com.jxz.java.demo.command.AddCommand"), ABORTED("abort", "com.jxz.java.demo.command.AbortCommand");

private String command;

private String clazz;

public static Map getAllClazz() throws IOException {

Set set = ClassResourcePatternResolver.findAllClassPathResources();

Iterator it = set.iterator();

while (it.hasNext()){

String fullName = it.next();

String command = fullName.substring(fullName.lastIndexOf(".")+1).replace("Command","").toLowerCase().trim();

DynamicEnumUtil.addEnum(CommandEnum.class,command,new Class>[] {String.class,String.class},new Object[] {command,fullName});

// map.put(command,fullName);

}

// for (CommandEnum commandEnum : CommandEnum.values()) {

// map.put(commandEnum.getCommand(), commandEnum.getClazz());

// }

Map map = new HashMap<>(8);

for (CommandEnum value:CommandEnum.values()){

String command = value.getCommand();

String clazz = value.getClazz();

map.put(command,clazz);

}

return map;

}

public String getCommand() {

return command;

}

CommandEnum(String command, String clazz) {

this.command = command;

this.clazz = clazz;

}

public void setCommand(String command) {

this.command = command;

}

public String getClazz() {

return clazz;

}

public void setClazz(String clazz) {

this.clazz = clazz;

}

}

至此整个优化完成,尝试新加一个逻辑处理类,类的命名方式:message类型+Command:package com.jxz.java.demo.command;

import com.jxz.java.demo.strategy.CommandStrategy;

/**

* @Author JiangXiaoZhi

* @Date 2019/3/18 19:51

* @ModifyDate 2019/3/18 19:51

* @Version 1.0

*/

public class DeleteCommand implements CommandStrategy {

@Override

public void process(String message) {

System.out.println("command type:"+message);

}

}

进行测试,结果如下:

164150015_2_20190621045040675_wm

结果正确,完。

==========================================================================

上面的代码不够完善,进一步优化,就是在每一次调用commandContext.getInstance(message)方法获取实例时,都要调用getAllClazz()方法扫描包获取类名,这一步是冗余的,扫描包下面的类只需要进行一次即可,可以用静态代码块,也可以用单例模式,此处用静态代码块:package com.jxz.java.demo.strategy;

import com.jxz.java.demo.util.ClassResourcePatternResolver;

import com.jxz.java.demo.util.DynamicEnumUtil;

import java.io.IOException;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

/**

* @Author JiangXiaoZhi

* @Date 2019/3/13 14:31

* @ModifyDate 2019/3/13 14:31

* @Version 1.0

*/

public enum CommandEnum {;

private String command;

private String clazz;

public static Map map = new HashMap<>(8);

static {

System.out.println("invoke static block......");

Set set = null;

try {

set = ClassResourcePatternResolver.findAllClassPathResources();

Iterator it = set.iterator();

while (it.hasNext()){

String fullName = it.next();

String command = fullName.substring(fullName.lastIndexOf(".")+1).replace("Command","").toLowerCase().trim();

DynamicEnumUtil.addEnum(CommandEnum.class,command,new Class>[] {String.class,String.class},new Object[] {command,fullName});

for (CommandEnum value:CommandEnum.values()){

String cmd = value.getCommand();

String claz = value.getClazz();

map.put(command,claz);

}

}

} catch (IOException e) {

e.printStackTrace();

}

}

public String getCommand() {

return command;

}

CommandEnum(String command, String clazz) {

this.command = command;

this.clazz = clazz;

}

public void setCommand(String command) {

this.command = command;

}

public String getClazz() {

return clazz;

}

public void setClazz(String clazz) {

this.clazz = clazz;

}

}

运行主程序,查看静态代码块值调用了一次:

164150015_3_20190621045040816_wm

完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值