好好的“代码优化”是怎么一步步变成“过度设计”的(上):https://developer.aliyun.com/article/1443490
继续拓展:设计模式
枚举的前提还是基于无状态前提,如果转换的的映射关系不再单纯,变得复杂,枚举的简单映射管理就不work了。
“万事不决,上设计模式”
哎~就是玩儿~
▐ 策略模式-简单实现
首先,依然将传入的字符串作为路由依据,但是传入的内容为了防止有未来扩展,所以构造一个上下文,策略本身基于上下文来处理,借助上文定义的值枚举做策略路由。
/** * 定义策略接口 */ public interface SaleTypeParseStrategy{ Integer parse(SaleTypeParseContext saleTypeParseContext); } /** * 策略实现 */ public class JxSaleTypeParseStrategy implements SaleTypeParseStrategy{ @Override public Integer parse(SaleTypeParseContext saleTypeParseContext) { return SaleTypeIntEnum.JX.getCode(); } } /** * 调用上下文 */ @Data public class SaleTypeParseContext{ private SaleTypeStrEnum saleTypeStr; private SaleTypeParseStrategy parseStrategy; public Integer pasre(){ return parseStrategy.parse(this); } } public static Integer parseSaleType(String saleTypeStr){ SaleTypeStrEnum saleTypeEnum = SaleTypeStrEnum.getByName(saleTypeStr); SaleTypeParseContext context = new SaleTypeParseContext(); context.setSaleTypeStr(saleTypeEnum); switch(saleTypeStr){ // 策略路由 case JX:context.setParseStrategy(new JxSaleTypeParseStrategy());break; // 继续扩展 default:return null; } return context.parse(); }
当然,如果是这种没有上下文强依赖的策略,无论是静态单例还是Spring单例都会是一个不错的选择。SaleTypeParseContext本身可以继续扩展内容和其他属性继续丰富参数,策略实现中也可以继续针对更多参数扩充逻辑。
▐ 策略工厂-手动容器
策略是个好东西,但是简单实现下,这里依然将策略实现的路由过程交给了调用方来做,那么每增加一种实现,调用点还要继续改,要是恰好有若干调用点就完犊子了,并不优雅,所以搞个中间层容器工厂,解耦一下依赖。
@Component public static class SaleTypeParseStrategyContainer{ public final static Map<SaleTypeStrEnum, SaleTypeParseStrategy> STRATEGY_MAP = new HashMap<>(); @PostConstruct public void init(){ STRATEGY_MAP.put(SaleTypeStrEnum.JX, new JxSaleTypeParseStrategy()); // 继续拓展 } public Integer parse(SaleTypeParseContext saleTypeParseContext){ return Optional.ofNullable(STRATEGY_MAP.get(saleTypeParseContext.getSaleTypeStr())).map(strategy-> strategy.parse(saleTypeParseContext)).orElse(null); } }
容器内手动创建各个策略的实现的单例后进行托管,那调用方只需要去构建上下文就好了,实际调用的方法更换为 SaleTypeParseStrategyContainer::parse,那后续无论策略如何丰富,调用方都不需要再感知这部分变化。后续出现了新的策略实现,则在工厂内继续追加路由表即可。
▐ 注册与发现&策略工厂-Spring容器
如果考虑到策略会依赖Spring的bean和其他有状态对象,那么这里也可以改成Spring的注入模式,同时继续将“支持哪种情况”由托管方容器移动至策略内部,改成由策略实现自身去注册到容器中。
public interface SaleTypeParseStrategy{ Integer parse(SaleTypeParseContext saleTypeParseContext); // 所支持的情况 SaleTypeStrEnum support(); } @Component public class JxSaleTypeParseStrategy implements SaleTypeParseStrategy{ @Override public Integer parse(SaleTypeParseContext saleTypeParseContext) { return SaleTypeIntEnum.JX.getCode(); } @Override public SaleTypeStrEnum support() { return SaleTypeStrEnum.JX; } } @Component public static class SaleTypeParseStrategyContainer{ public final static Map<SaleTypeStrEnum, SaleTypeParseStrategy> STRATEGY_MAP = new HashMap<>(); @Autowired private List<SaleTypeParseStrategy> parseStrategyList; @PostConstruct public void init(){ parseStrategyList.stream().forEach(strategy-> STRATEGY_MAP.put(strategy.support(), strategy)); } public Integer parse(SaleTypeParseContext saleTypeParseContext){ return Optional.ofNullable(STRATEGY_MAP.get(saleTypeParseContext.getSaleTypeStr())).map(strategy-> strategy.parse(saleTypeParseContext)).orElse(null); } }
这样的话,连容器都不用改了,追加策略实现的改动只与当前策略有关,调用方和容器类都不需要感知了,但是缺点就在于如果有俩策略支持的情况相同,取到的是哪个就听天由命了~
▐ 注册与发现&责任链
当然如果不能事先知道“支持哪种情况”,只能在运行时判断“是否支持”,将事前判定改为运行时判定,广义责任链会是一个不错的选择,把所有策略排成一排,谁举手说自己能处理就谁处理。
public interface SaleTypeParseStrategy{ Integer parse(SaleTypeParseContext saleTypeParseContext); // 用于判断是否支持 boolean support(SaleTypeParseContext saleTypeParseContext); } @Component public class JxSaleTypeParseStrategy implements SaleTypeParseStrategy{ @Override public Integer parse(SaleTypeParseContext saleTypeParseContext) { return SaleTypeIntEnum.JX.getCode(); } @Override public boolean support(SaleTypeParseContext saleTypeParseContext) { return SaleTypeStrEnum.JX.equals(saleTypeParseContext.getSaleTypeStr()); } } @Component public static class SaleTypeParseStrategyContainer{ @Autowired private List<SaleTypeParseStrategy> parseStrategyList; public Integer parse(SaleTypeParseContext saleTypeParseContext){ return parseStrategyList.stream() .filter(strategy->strategy.support(saleTypeParseContext)) .findAny() .map(strategy->strategy.parse(saleTypeParseContext)) .orElse(null); } }
这样的实现,依然可以将改动收束在策略本体上,修改相对集中,可以无耦地进行扩展。
其他拓展
以上还只是在JAVA语言内去玩一些花样,在当前这种场景下肯定是有过度设计的嫌疑,7行代码可以缩到1行,也可以扩充到70行,所以说嘛:
“用代码行数来考量一个程序员是不太合适滴!~”当然了,也还可以继续借助其他的中间件搞花样,包括但不限于:
- 植入Diamond走走动态配置开关的思路;
- 植入QLExpress搞搞逻辑表达式的思路;
- 把策略实现改成HsfProvider走分布式调用思路;
- 借助一些成熟的网关走服务路由的的调用思路;
就不再此再过多展开了。