最近开始重新看《Head First 设计模式》,作为一个不错的练习,打算在整理设计模式笔记的时候用Python实现。
作为第一个介绍的设计模式,策略模式简单的同时相当实用。
在这里,我不打算采用书中的鸭子作为例子,我决定使用我喜欢的军事题材来描述,我们不做鸭子,造航母!
航母游戏
我们现在考虑如何做一个航母游戏,就相当于航母版的战舰世界吧,这里只有一类船:航空母舰。
我们考虑一下,航母最重要的共能无疑是起降战机,目前世界上的现代航母,无非采用以下几种方式起降:
-
弹射起飞阻拦着舰,比如美帝的尼米兹系列。
-
滑跃起飞阻拦着舰,比如我们的辽宁舰。
-
垂直起飞垂直着舰,比如阿三的几艘。
-
滑跃起飞垂直着舰,不错,这么奇葩的只有我大嘤帝国。
其中起飞模式有三种,着舰模式有两种。
凡是学过OOP的,第一直觉肯定是构建一个这样的类图:
这很容易想到,但是,这样做没有任何代码复用,我们需要在每个子类中分别实现起飞和着舰的内容,现在我们只是创建了四个子类,如果要加入日本、意大利、泰国之类的小航母,那无疑是场灾难。
代码复用的基石是将可可变且可复用的部分进行剥离和封装。
这也是非常实用的一种设计模式准则。
如我们之前讨论的,起飞和降落都是有数的几种模式,那我们是不是可以将这些起飞、降落模式从具体的航母上剥离?
如果你是Java程序员,那肯定会想到接口。不错,我们可以通过接口将这几种起降模式进行剥离和封装。
可能这个类图并不怎么美观,不过我已经尽了最大努力。
我们可以从类图上看到,我们已经把降落和起飞逻辑从航母具体实现中剥离,分别使用两组类来实现,然后再通过组合到航母基类的方式来使用这些起、降落模式。
我们现在用Python来具体实现。
代码有点多,这里就不一一展示了,工程文件直接上传到百度云:
链接:https://pan.baidu.com/s/1sGrKywSRwYEhbVr5fKg68w 提取码:1c4b 复制这段内容后打开百度网盘手机App,操作更方便哦
我们现在运行一下:
辽宁舰 进行滑跃起飞 进行阻拦着舰 尼米兹级 进行弹射起飞 进行阻拦着舰 伊丽莎白女王号 进行滑跃起飞 进行垂直降落着舰 维克拉马蒂亚号 进行垂直起飞 进行垂直降落着舰
可以看到不同类型的航母都按自己的方式起飞和着舰,而且无论新增哪个国家的哪艘航母,我们都可以按照已经构建的飞行和着舰模式进行快速构建。
而且这样做还有额外的好处,比如我们如果需要对辽宁号进行改造,安装弹射器,将起飞模式改为弹射起飞,我们只需要这样做:
在基类中加入改变起飞模式的方法:
def setTakeoffMode(self, takeoffMode):
self._takeoffMode=takeoffMode
然后修改主程序:
lnCarrier = LiaoNingCarrier()
print(lnCarrier)
from takeoff_pkg.catapult_takeoff import CatapultTakeoff
lnCarrier.setTakeoffMode(CatapultTakeoff())
lnCarrier.takeoff()
lnCarrier.land()
我们只需要更换辽宁号的起飞模式就能轻松实现。
总结一下,我们刚才进行的一系列OOP封装使用的就是一种设计模式,称为策略模式。
策略模式
顾名思义,策略模式指的是将一系列相似的策略封装为一组类,以起到复用的效果。
这里有三个关键的设计模式准则:
-
抽取可变的部分进行封装。
-
多用组合,少用继承。
-
针对接口编程,而非实现。
这三点其实是互相关联的,比如我们如果要把一组类中的可复用部分进行封装,可以选择的手段只有继承和组合。而继承是一种很笨重的方式,一来它和子类紧密结合,而且随着继承层级的增加继承树就会很复杂,基类和各级别子类之间的重写继承关系就很难维护和处理。而组合就相当灵活,还可以随时替换。
而为了实现组合,那必然会用到接口或者抽象基类,针对组合这个场景来说,我们其实并不需要关心具体是用接口实现还是抽象基类,其本质都是利用你他们的多态特性持有相应的句柄,以进行多态调用。
文章评论