当代码中出现很多if-else语句,并且每个if代码块中的逻辑都很复杂的时候,这时如果要对代码进行维护或者是让别人使用你的代码都会比较麻烦,而且有可能会因为一点改动导致整个系统出现异常。所以需要考虑如何对大量的if-else进行重构,让各个if-else的逻辑独立开来,互不影响。
这里想到了设计模式当中的策略模式,定义一个策略接口,每个if代码块都是一种策略的实现类,然后定义一个策略上下文,目的是根据不同的需求,即if条件,来选择不同的策略的实现。
现在,我提供一个在工作中出现的场景demo:
在一个文字游戏客户端中,我需要根据玩家输入的字符串来判断玩家需要执行的指令,例如user_login代表用户登录,客户端根据指令返回对应的protobuf类,然后发送给服务器端。这里因为我需要使用指令来映射protobuf类,因此用到了静态工厂模式,利用Spring的IOC管理每种策略实现,将每一个指令-protobuf类映射注册为一种产品,然后使用静态工厂来生产这种产品。
代码如下:
策略模式
/**
* 指令策略接口
*
* @author Administrator
*/
public interface CommandStrategy {
/**
* 处理过程, 根据对应的proto类 来调用不同的策略去执行proto文件的封装
* @param content 玩家输入的参数,除指令以外的信息
* @return
* @throws Exception 参数错误等异常
*/
Object processProto(String content) throws Exception;
}
/**
* 处理UserLoginReq的Proto的策略实现类
*
* @author Administrator
*/
@Component("userLoginReqProto")
public class UserLoginReqProto implements CommandStrategy, InitializingBean {
@Override
public UserLoginReq processProto(String content) throws Exception {
if (StringUtils.isEmpty(content)) {
throw new Exception("参数不能为空");
}
String[] split = content.split("\\s+");
if (split.length != 2) {
throw new Exception("参数个数出错!");
}
UserLoginReq userLoginReq = UserLoginReq
.newBuilder()
.setAccount(split[0])
.setPassword(split[1])
.build();
return userLoginReq;
}
/**
* 静态工厂:注册为静态工厂的一种产品
*/
@Override
public void afterPropertiesSet() throws Exception {
CommandFactory.registerReqClass(Command.USER_LOGIN.getCmd(), this);
}
}
静态工厂模式
/**
* 静态工厂,根据指令生产对应的Proto对象
*
* @author Administrator
*/
public class CommandFactory {
/**
* 策略CommandStrategy的具体对象,通过策略对象返回对应的proto req
*/
private static Map<Integer, CommandStrategy> protoMap = new HashMap<>();
private CommandFactory() {}
/**
* 根据指令得到CommandStrategy对象
* @param cmd
* @return
*/
public static CommandStrategy getReqClass(Integer cmd) {
return protoMap.get(cmd);
}
/**
* 将具体CommandStrategy对象注册到Map
*/
public static void registerReqClass(Integer cmd, CommandStrategy proto) {
protoMap.put(cmd, proto);
}
}
/**
* 静态工厂,返回Proto Req的具体对象
* 根据指令获取对象实例
* @author Administrator
*/
public class WorkReqProto {
/**
* 返回Proto Req的具体对象
*
* @param cmd 需要返回的proto类型
* @param content 输入的字符串参数内容,如wan 123456
* @return
*/
public static Object getReqProto(Integer cmd, String content) throws Exception {
CommandStrategy commandStrategy = CommandFactory.getReqClass(cmd);
return commandStrategy.processProto(content);
}
}