乐趣区

关于设计模式:HeadFirst设计模式读书笔记一策略模式

作者:折纸
集体博客:https://www.zhezhi.press
记录菜鸡的成长之旅!

1 从一堆鸭子说起

家喻户晓《机器学习》(周志华版)被称为西瓜书,我感觉《Head First 设计模式》也能够被称为鸭子书~ 假如有这么一个模仿鸭子游戏 SimUDuck,游戏中会呈现各种鸭子,它们一边游泳一边呱呱叫。于是有这么一个鸭子超类,并且让各种鸭子继承这个超类;


这看起来仿佛还不错,所有的鸭子都会继承 Duck 超类,并且只须要重写 display 办法以打印本人的特色就能够。但 需要是在变动的

2 让鸭子飞起来!

好吧,看起来不难,咱们只须要给 Duck 类加一个 fly()办法并实现就能够了;的确这样一来所有继承 Duck 类的鸭子都会飞了,然而咱们疏忽了一个事实——不同子类之间的差异性,橡皮鸭子本来不会飞当初飞起来了!

对代码的部分批改 影响的不肯定只停留在部分;当波及“保护”时,为了“复用”目标而应用继承,终局并不完满
我的了解:对超类的批改要审慎,防止造成不必要的麻烦!如无必要,勿增实体

2.1 解决办法:子类重写

咱们能够重写不会飞的橡皮鸭子的 fly 办法,将其改为什么也不做就能够了。
这的确解决了橡皮鸭能够飞的问题,然而当前每次设计新的子类的时候,咱们都须要查看 fly、quack、swim 等办法,这就失去了复用的意义。

通过 继承 提供 Duck 的 行为,可能会带来的毛病

代码在多个子类中反复(须要改写行为以适应不同子类)
运行时的行为不容易扭转(无奈灵便切换)
很难晓得所有鸭子的全副行为
扭转会牵一发而动全身,导致其余鸭子不想要的扭转

2.2 解决办法:通过接口

接口是对行为的形象,能够把 fly()、quack()从超类中提取进去,放进 Flyable、Quackable 接口,而后只有会飞、会叫的鸭子才回去实现这个接口。
但这一来反复的代码将变得更多,对于每一种会飞、会叫的鸭子都须要去实现对应的接口,此外还有不同航行办法、不同叫声的叫法,这使得代码的可复用性降得很低。

2.3 从新扫视问题

首先咱们晓得在这个案例中,fly 是新退出的需要~
并且应用继承并不能很好地解决问题, 因为鸭子的行为在子类里一直地扭转, 并且让所有的子类都有这些行为是不失当的。Flyable 与 Quackable 接口一开始仿佛还挺不错, 解决了问题 (只有会飞的鸭子才继承 Flyable), 然而 Java 接口不具备实现代码, 所以继承接口无奈达到代码的复用。
这意味着: 无论何时你须要批改某个行为, 你必须得往下追踪并在每一个定义此行为的类中批改它, 一不小心, 可能会造成新的谬误!
侥幸的是, 有一个设计准则, 恰好实用于此情况。

设计准则: 找出利用中可能须要变动之处, 把它们独立进去, 不要和那些不须要变动的代码混在一起。

上面是这个准则的另一种思考形式:“把会变动的局部取出并封装起来, 以便当前能够轻易地改变或裁减此局部, 而不影响不须要变动的其余局部”。–> 零碎变得更有弹性

这样的概念很简略, 简直是每个设计模式背地的精力所在 。所有的模式都提供了一套办法让“零碎中的某局部扭转不会影响其余局部”。
好, 该是把鸭子的行为从 Duck 类中取出的时候了!

3 抽离易于变动的局部

一种办法是建设 2 组齐全远离 Duck 类的 Class,每一组类将实现 fly、或者是 quack,而类中有对于 fly 的不同实现;而且能够使得鸭子的行为能够动静地扭转(setter)

设计准则:针对接口编程,而不是针对实现编程。

咱们利用接口代表每个行为,行为的每个实现都将实现其中的一个接口。这样的做法迥异于以往, 以前的做法是: 行为来自 Duck 超类的具体实现, 或是继承某个接口并由子类自行实现而来。

这两种做法都是 依赖于“实现”, 咱们被实现绑得死死的, 没方法更改行为 (除非写更多代码)。在咱们的新设计中, 鸭子的子类将应用接口(FlyBehavior 与 QuackBehavior) 所示意的行为, 所以理论的“实现”不会被绑死在鸭子的子类中 ( 鸭子不会负责实现 Flyable、Quackable 接口)。(换句话说, 特定的具体行为编写在实现了 FlyBehavior 与 QuakcBehavior 的类中,从而实现理解耦)。

这样的设计,能够让航行和呱呱叫的动作被其余的对象复用, 因为这些行为曾经与鸭子类无关了;并且咱们也能够新增一些行为(不同的实现),不会影响到已有的行为类和“应用”其余已有行为的鸭子类。

这么一来, 有了继承的“复用”益处, 却没有继承所带来的包袱。

4 复盘

你兴许可能听过 Has-A is Better than is-A 的说法,兴许你可能像我一样此前并没有很间接地感触到或者思考过 Why Has-A is Better than is-A?
但通过鸭子的例子我想你应该有一些感触了,is- A 也就是继承的关系(不同的鸭子继承超类 Duck),而 has- A 更偏差于组合的关系(鸭子类有 FlyBehavior 类和 QuackBehavior 类,鸭子是容器 / 对象,而后两者是容器 / 对象的组件类)

设计准则:多用组合,少用继承

如你所见,应用组合建设零碎具备很大的弹性,不仅可将 算法族 (对于同一个行为的不同实现办法) 封装成类,更能够“在运行时动静地扭转行为”(setter 办法),只有组合的行为对象合乎正确的接口标准即可。
组合用在许多设计模式中, 它也有许多长处和毛病, 在之后的学习中咱们还会持续探讨;

5 策略模式

策略模式是一种行为型设计模式, 它能够在运行时抉择算法; 与间接实现某个算法不同, 代码在运行时能够抉择在算法族中选取一个执行。
(In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.wikipedia)

或者说

策略模式将某一个行为 (接口)通过不同的办法 / 算法实现[上文所提的算法族],不便须要实现该接口对应行为[Has-A] 的程序 / 客户能够在 ” 运行时 ”灵便切换 该行为实现的办法 / 策略,使得策略的变动独立于应用它的客户 / 程序;

退出移动版