《HeadFirst设计模式》读书笔记一:策略模式

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

1 从一堆鸭子提及

众所周知《机器学习》(周志华版)被称为西瓜书,我以为《Head First 设计模式》也能够被称为鸭子书~ 假设有这么一个模拟鸭子游戏SimUDuck,游戏中会出现各类鸭子,它们一边游泳一边呱呱叫。因而有这么一个鸭子超类,而且让各类鸭子继承这个超类;算法

image.png
这看起来彷佛还不错,全部的鸭子都会继承Duck超类,而且只须要重写display方法以打印本身的特征就能够。但需求是在变化的编程

2 让鸭子飞起来!

好吧,看起来不难,咱们只须要给Duck类加一个fly()方法并实现就能够了;确实这样一来全部继承Duck类的鸭子都会飞了,可是咱们忽略了一个事实——不一样子类之间的差别性,橡皮鸭子本来不会飞如今飞起来了!设计模式

对代码的局部修改 影响的不必定只停留在局部;当涉及“维护”时,为了“复用”目的而使用继承,结局并不完美
个人理解: 对超类的修改要谨慎,避免形成没必要要的麻烦!如无必要,勿增实体

2.1 解决办法:子类重写

咱们能够重写不会飞的橡皮鸭子的fly方法,将其改成什么也不作就能够了。
这确实解决了橡皮鸭能够飞的问题,可是之后每次设计新的子类的时候,咱们都须要检查fly、quack、swim等方法,这就失去了复用的意义。机器学习

经过继承提供Duck的行为,可能会带来的缺点学习

代码在多个子类中重复(须要改写行为以适应不一样子类)
运行时的行为不容易改变(没法灵活切换)
很难知道全部鸭子的所有行为
改变会牵一发而动全身,致使其余鸭子不想要的改变

2.2 解决办法:经过接口

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

2.3 从新审视问题

首先咱们知道在这个案例中,fly是新加入的需求~
而且使用继承并不能很好地解决问题,由于鸭子的行为在子类里不断地改变,而且让全部的子类都有这些行为是不恰当的。Flyable与Quackable接口一开始彷佛还挺不错,解决了问题(只有会飞的鸭子才继承Flyable),可是Java接口不具备实现代码,因此继承接口没法达到代码的复用。
这意味着: 不管什么时候你须要修改某个行为,你必须得往下追踪并在每个定义此行为的类中修改它,一不当心,可能会形成新的错误!
幸运的是,有一个设计原则,刚好适用于此情况。设计

设计原则: 找出应用中可能须要变化之处,把它们独立出来,不要和那些不须要变化的代码混在一块儿。

下面是这个原则的另外一种思考方式:“把会变化的部分取出并封装起来,以便之后能够轻易地改动或扩充此部分,而不影响不须要变化的其余部分”。-->系统变得更有弹性code

这样的概念很简单,几乎是每一个设计模式背后的精神所在。全部的模式都提供了一套方法让“系统中的某部分改变不会影响其余部分”。
好,该是把鸭子的行为从Duck类中取出的时候了!对象

3 抽离易于变化的部分

一种方法是创建2组彻底远离Duck类的Class,每一组类将实现fly、或者是quack,而类中有对于fly的不一样实现;并且可使得鸭子的行为能够动态地改变(setter)继承

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

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

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

这样的设计,可让飞行和呱呱叫的动做被其余的对象复用,由于这些行为已经与鸭子类无关了;而且咱们也能够新增一些行为(不一样的实现),不会影响到已有的行为类和“使用”其余已有行为的鸭子类。

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

image.png

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]的程序/客户能够在"运行时" 灵活切换该行为实现的方法/策略,使得策略的变化独立于使用它的客户/程序;