Head First 设计模式笔记 1.策略模式
作者:互联网
文章目录
摘要
这篇文章将通过一个鸭子的设计修改过程,讲解一点关于策略模式的知识以及一些常用的设计原则。
继承超类的设计
小明接到甲方爸爸订单需要设计一只鸭子,这只鸭子要求会叫,游泳,显示在屏幕中。小明想,这还不简单,采用继承的思想来做。创建一个Duck超类,让各种鸭子继承该超类。它具有叫方法quack,游泳方法swim,显示方法display,如下图所示。
结果甲方爸爸提出意见:“我们需要一只会飞的鸭子。”作为一名优秀的程序员,小明表示这有什么难的。大笔一挥,在超类中加上一个fly()方法,让所有鸭子继承它就可以了。
但是,悲剧发生了,在会议上,甲方爸爸意外地发现一只橡皮鸭RubberDuck在飞来飞去。原来,并非所有的鸭子都会飞。小明想,这没什么。在橡皮鸭中覆盖迪掉fly()方法,改为什么都不做就好了。
然而,问题在于,以后小明再加入一些鸭子,都要去检查fly()方法和quack()方法,并且必要时候覆盖它们。例如加入不会叫也不会飞的木头鸭子DecoyDuck,就需要将这两个方法全部覆盖。这简直就是地狱。
这里小明发现了继承提供Duck行为的问题:“牵一发而动全身,修改父类,会导致子类产生不需要的改变”
。
实现接口的设计
小明想了想,决定把fly()这个变化的量从超类中抽出,放进"Flyable接口",这样,只有会飞的鸭子才实现这个接口。同理,可以设计一个"Quackalbe接口",并非所有鸭子都会叫。
但是这样并不能解决问题,接口不能提供代码的实现,无法达到代码复用
。在每个子类都必须要考虑实现Flyable和Quack中的方法。这同样让小明头疼。
采用设计模式
继承超类的设计虽然能够实现代码复用,但是不方便修改。而实现接口的设计虽然能灵活组合各种需要,但是却不能实现代码复用。
为了解决这个问题,我们需要用到第一个设计原则:
找出应用中可能需要变化之处,把他们独立出来,不要把它们和不会变化的代码混在一起
这里鸭子类的变化部分是飞行行为和呱呱叫行为(当然,有人会认为游泳也是),我们将这两个部分抽出。将这两个行为用类而不是用方法来表示。
设计鸭子的行为
为了增加鸭子行为的弹性,我们可以考虑将鸭子的行为设置为可以改变的。在鸭子类中加一个设定类setBehaviour的方法。这样就可以在运行的时候改变鸭子的行为。为了满足这个目标,我们有了第二个设计原则:
针对接口编程,而不是针对实现编程。
所谓针对接口编程,就是说,变量的声明类型应该是超类型,通常是接口,如此,只要具体实现这个超类型的对象,都可以赋值给这个变量。声明类的时候,不需要考虑真正对象的类型。也就是多态。
基于这种思想,我们设计两个接口FlyBehavior和QuackBehavior,用来作为引用变量。而它们有对应的类,负责具体的行为。
整合鸭子
最后的代码如下
package headfirst.designpatterns.strategy;
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {
}
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
abstract void display();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
这里我们采用了组合而不是继承的方法实现飞行和鸣叫两个动作,从而实现了较高的灵活性,不仅可以将算法封装成类,而且可以在运行的时候动态改变行为,我们得到了第三个设计原则
多用组合,少用继承
同时,恭喜你学到了第一个设计模式——策略模式
策略模式定义了算法族,分别封装起来,让它们之间可以互换,此模式让算法的变化独立于使用算法的客户。
标签:fly,quack,Head,接口,鸭子,Duck,设计模式,display,First 来源: https://blog.csdn.net/zhazha_hui/article/details/111798013