丑陋的模样
public Fruit buyFruit(String name) {if ("苹果".equals(name)) {return new BuyApple().buy();} else if ("香蕉".equals(name)) {return new BuyBanana().buy();} else if ("西瓜".equals(name)) {// 买西瓜if (true) {// todo} else {// todo}}return null;}
- 分支判断越来越多,方法就会越来越大,就算每个分支压缩到一个function,当超过一定数量也会很难看。最重要的是破坏了设计的开闭原则。
变美步骤
第一步 - 基本预期
思考:这个功能,这个方法改动到底是不是很频繁,新增的判断逻辑(if else)将来会不会很频繁。有个预期就行,如果没有预期,那就看实际改动次数。
- 如果不频繁,建议就这样吧,只要结构清晰,方便review代码就行。不鼓励过度设计,毕竟开发时间都是有限的。
- 如果很频繁,就看第二步
第二步 - 梳理核心逻辑
判断当前功能是不是都是由一个策略值,来决定后面整体流程,并且每个流程都不太相同,这样容易想到可以使用策略模式来进行优化。
如果当前功能不光有不同策略处理流程,还有共同的处理流程,那么可以把不同策略处理流程抽取出来,使用策略模式进行优化,共同的部分不在本文讨论范围内。
第三步 - 重构
只讨论使用spring项目
给策略进行定义,比如水果场景
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitScene {String value();
}
给所有策略对应的处理流抽象成一个方法,取个名字,比如买水果
public interface BuyFruit {Fruit buy();
}
定义每个策略对应处理流程,比如买苹果、买香蕉
@Component
@FruitScene("苹果")
public class BuyApple implements BuyFruit {@Overridepublic Fruit buy() {System.out.println("买苹果");return new Fruit("苹果");}
}
@Component
@FruitScene("香蕉")
public class BuyBanana implements BuyFruit {@Overridepublic Fruit buy() {System.out.println("买香蕉");return new Fruit("香蕉");}
}
- 将来新增处理流程只需要新增一个类、方法
把定义的策略场景和处理流程进行关联
@Component
public class BuyFruitFactory {private static Map<String, BuyFruit> getBuyFruitMap = new HashMap<>();@Autowiredprivate void setBuyFruitMap(List<BuyFruit> buyFruitList) {buyFruitList.forEach(fs -> {FruitScene fruitScene = fs.getClass().getAnnotation(FruitScene.class);if (fruitScene != null) {getBuyFruitMap.put(fruitScene.value(), fs);}});}public static Fruit buy(String fruitName) {return getBuyFruitMap.get(fruitName).buy();}
}
最终使用方式 - 优雅的一行代码
public Fruit buyFruit2(String name) {return BuyFruitFactory.buy(name);}
点题
看起来整体代码变得更复杂了?哪里体现了开放封闭?
上面的例子只有两种策略,所以看起来代码变多了。如果策略很多,实际上重构后的代码基本都是固定的(无需更改),每次有新的策略流程,只需要实现BuyFruit接口即可(步骤2),做到了对扩展是开放的,而对修改是封闭的。