总结:策略模式对对象与行为进行了解耦,增加类的可扩展性,使我们对类的扩展更加符合开闭原则。
忽然对设计模式有了很多感悟,原来对设计模式的学习也费了不少功夫,但始终感觉轻飘飘的,抓不住重点。
最近重构代码的任务比较多,随着对代码结构的重新设计,很多之前不明白的地方忽然就通了。
从优化代码结构的角度出发,才能对设计模式有更加深刻的体会。软件与制造、建筑业一样,是一个工程化的行业。我们应该注重工程的模块化与标准化,设计出的功能是否易维护、可扩展,是区分“开发者”与“工程师”的重要标准。设计模式为我们在对客观世界建立抽象时提供了一系列的参考,如果只是站在实现功能的层面看设计模式,只能感受到它的琐碎与晦涩;但从整体结构设计的层面看,便可以感受到抽象之美与结构之美。
之前在代码的设计上,最注重的是对模块的解耦,也就是遵循依赖倒置原则。在定义功能时首先定义其接口,也就是规范。调用方与被调用方都去依赖规范而不去依赖具体实现,做到面向抽象编程。相对于具体实现,抽象更加稳定,这使我们在抽象不变的情况下,减少了改变具体实现时的代价。
除此之外我也会经常注意代码的模块化,比如通过 Decorator 将不同的功能隔离成不同的模块,按需拼装;或是通过责任链对功能进行分割以便进行整体逻辑的变动与节点模块的复用。或是在协作过程中通过适配器或代理对同事写的模块进行适配,在保证其原有逻辑的基础上适配新的场景。单例模式/工厂模式/观察者模式/模板模式也自不必说,在实际生产过程中会经常的使用。
但是策略模式使用的很少,之前总是从解耦的角度理解策略模式,现在才发现理解策略模式应该从开闭原则去理解。策略模式使得我们的代码更易扩展,与解耦调用者和被调用者是完全两码事。
策略模式将对象本身和行为分成了两部分,我们可以通过向对象注入不同的行为来进行对象行为的扩展,并且不需要改变对象的原有实现。
比如我们有一个负责实现 “娱乐” 行为的类,“娱乐” 这一行为有多种方式,可以骑车、泡吧、看电影、看书等等,如果不使用策略模式,我们可能需要一大堆 if else 判断当前娱乐的方式,并且在增加一种娱乐方式时,我们需要增加一个 if else 节点,这可能会对原有逻辑造成破坏。
在策略模式下,我们则可以将行为与对象完全分开,娱乐方式的改变不会影响原有对象的逻辑。
比如我们定义一个娱乐策略的接口:
/** * @Author Nxy * @Date 2020/3/15 19:28 * @Description 娱乐策略 */ public interface PlayStrategy { public String play(); }
以该接口为标准实现两种娱乐方式:
/** * @Author Nxy * @Date 2020/3/15 19:30 * @Description 泡吧 */ public class BarStrategy implements PlayStrategy { @Override public String play() { return "娱乐方式:泡吧"; } }
public class BikeStrategy implements PlayStrategy { @Override public String play() { return "娱乐方式:骑自行车"; } }
负责实现 “娱乐” 的主类依赖娱乐接口,按照注入的具体对象完成娱乐的行为:
/** * @Author Nxy * @Date 2020/3/15 20:00 * @Description 娱乐实现类 */ public class Play { PlayStrategy strategy; public Play(PlayStrategy strategy) { this.strategy = strategy; } void play() { System.out.println(strategy.play()); } }
我们进行一下测试:
public class Demo { public static void main(String[] args) { Play barPlay = new Play(new BarStrategy()); Play bikePlay = new Play(new BikeStrategy()); barPlay.play(); bikePlay.play(); } }
如果需求发生变动,我们需要新的娱乐方式:读书,那么我们只需要新增娱乐策略:
/** * @Author Nxy * @Date 2020/3/15 20:54 * @Description 娱乐方式:读书 */ public class ReadStrategy implements PlayStrategy { @Override public String play() { return "娱乐方式:读书"; } }
在完全不改变 Play 和不影响其它娱乐策略的情况下即可完成策略的新增: