策略模式解析
这是从零学习设计模式的第一篇文章,也算是入门篇吧。
大家都知道设计模式很重要,掌握设计模式是一个高级开发乃至架构师都必须要具备的技能。但是不知道有没有这样的感觉,每次看设计模式的概念都差不多能看懂,但是就是不会用,而且不能够识别一些优秀的代码具体是运用了什么设计模式?最终的结果就是,慢慢的忘记,脑海中的设计模式只是一个很大很虚的概念。
我目前就属于这种状态,为了能够提高自己的编码设计能力,在工做中设计出有弹性,好维护,能更好的应付业务变化的代码,准备认真学习一下设计模式。
其实大学期间就断断续续看过《Head First 设计模式》,但是当时印象也不深刻,工作后也不太会用。到现在工作快四年了,再次看这本书,感觉真的不一样,也是因为工作这几年有了实战经验,慢慢知道了平时开发中的痛点,最后优化形成的解决方案,其实有很多就是用到了设计模式的思想。
目标
作为 Java 程序员,我们已经进入到对象村,OO思想和设计是已经深入人心的潜规则。但是 良好的 OO 设计,并不是那么简单的,要结合前人大量的实践经验总结,不断的艰苦练习才能成功。而这些前人的经验就是已经整理好的设计模式。
使用设计模式最好的方式是:“把模式装进脑子里,然后在你的设计和已有的应用中,寻找何处可以使用它们”。平时我们做的最多的是代码复用,而学习设计模式其实是经验复用。
一定要多回顾、多思考、多实践,这样才能深入理解和使用好设计模式。
入门:鸭子系统的设计
中级程序员的设计实现
我们来看一个简单的例子,鸭子系统的设计严谨,去理解设计模式带来的影响。
有一款模拟鸭子游戏,游戏中有很多种鸭子,会游泳,会呱呱叫,这时候利用 标准的 OO 技术,设计如下:
继承来实现
游泳和叫所有鸭子都会,所以为了代码复用,在基类中实现,外观由于各不相同是抽象方法,下沉到子类去实现,ok,符合 OO 思想,到此就结束了?那只能说你太年轻。。
需求变更:主管确定要求游戏中有会飞的鸭子,这时候你评估需求时,信誓旦旦的说,我们三天搞定。。。嗯。。加下班,明天就可以上线。
毕竟作为一名 OO 程序员,这没什么困难的,你直接在鸭子的基类中添加一个 fly() 方法,然后就被人类继承了, OO 大显神功。
但是,这个时候问题出现了,领导在测试环境做体验的时候,发现以前的 "僵尸鸭子" 居然也在天上飞来飞去,这下炸了,对代码局部的修改,居然影响到了全局,让一些没有生命力的鸭子也会飞上天了。
这个时候,你可能想到继承,可以在子类中覆盖父类的实现,将 “僵尸鸭子”的 fly() 非法覆盖掉,改为不会飞 。。。
这样虽然也可以,但是你要想到,如果后续又加入了 “诱饵鸭” 即不会飞也不会叫,这难道也要去其实现中重写对应的方法??
要知道作程序员,你一定要牢记一个不变的真理,那就是 CHANGE!不管一开始设计的多么好,一段时间后,总是需要成长与改变。
到这里你也发现了,继承中的缺点是:
代码在多个子类中重复;
运行时的行为不容易改变,必须通过硬编码;
很难知道所有鸭子的全部行为,不同的子类实现不一样;
改一处就会动全身,造成人类鸭子不想要的改变;
把行为设计成接口
你可能会想到,将飞行行为和呱呱叫设计为对应的接口,有对应需要的鸭子直接去实现就好了,虽然这个方法是可以解决问题,但是你要想到,后续鸭子越来越多,每个都需要去实现飞行和呱呱叫接口,这样得要写多少重复的代码,无疑又掉入了另一个坑中。
模式大师来设计
找出应用中可能需要变化的,把它们独立出来进行封装
如果找到代码中可能需要变化的呢?
每次新需求来了,都会使某方面的代码发生变化,那么就可以确定,这部分代码需要被抽出来,和其他稳定的代码区分;
凭借自己对需求的理解,在一开始设计时将未来需求改变的代码抽出来封装;
这其实就是很多设计模式背后的精神所在,也是一种设计原则:“将会变化的部分取出来并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分”。
所以,结合鸭子系统的需求,我们可以发现,鸭子的行为是经常变化的,因为有的会飞有的不会飞,有的会叫有的不会叫,所以需要抽出来进行封装。而其他部分还是比较正常的,所以依旧放到 Duck 基类中。
针对接口编程,而不是针对具体实现
上面我们已经知道了要将鸭子的 飞行和呱呱叫行为 抽出来封装,那么该如何实现呢?
我们的设计目标就是一切都有弹性,避免出现 中级开发工程师设计的系统,由于没有弹性所以才去请了模式大师出手。
大师将鸭子的行为设计成接口,如 FlyBehavior、QuackBehavior,这样行为就可以动态的扩展,我们可以在鸭子类中包含行为的方法,然后在 “运行时” 动态的 “改变” 飞行和呱呱叫行为。
也就是在,大师的设计中,鸭子的子类将使用接口(FlyBehavior 和 QuackBehavior)所表示的行为,所以实际的实现不会被绑死在鸭子的子类中。
而且我们也可以新增一些行为,不会影响到既有的飞行和呱呱叫行为,也不会影响 “使用”到已有行为的鸭子类,
多用组合,少用继承
相信这个概念你不是第一次听到了,组合优于继承,因为使用组合建立的系统拥有很大的弹性,可以“在运行时动态的改变行为”,因为结合上面的针对接口编程,只要组合的行为符合接口定义标准即可。
下面我们一起来看下,最终的系统结构:
认真去看下设计图,代码应该很好实现了,我就不写了。
设计模式:策略模式
上面大师的设计,就引出了我们今天要学习的设计模式:策略模式。不要怀疑,正是使用了策略模式改写了我们的鸭子系统。
策略模式定义了算法簇,分别分装起来,让它们之间可以互相转换,此模式让算法的变化独立于使用算法的客户。
思考与提升
我们如何使用设计模式?
我们开发中都是使用设计好的库和框架,这个没有问题,我们享受着别人代码的便利性,但是如何组装到我们的应用系统中,使我们的应用系统变得 易了解、容易维护、具有弹性,这个就需要设计模式出马了。但是设计模式不会直接进入你的代码中,你要做的就是先让设计模式进入你的大脑中,这样在开始新设计又或者当旧代码一团乱麻时,你就可以运用它们。
一开始想不到可用的设计模式?
这个是我们初学者必然遇到的问题,别慌,先牢记一些面向对象的原则,因为它们适用于所有模式,当我们踩过坑,不断思考这些原则最终的方案往往就是设计模式了。
OO基础:
抽象
封装
多态
继承
本文涉及的OO原则(后续还有更多):封装变化 多用组合,少用继承 针对接口编程,不针对实现编程
OO模式:策略模式: 定义算法族,分别分装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
参考资料:
《Head First 设计模式》
《设计模式:可复用面向对象软件的基础》
评论