Head First 设计模式笔记 1.策略模式

文章目录

摘要

这篇文章将通过一个鸭子的设计修改过程,讲解一点关于策略模式的知识以及一些常用的设计原则。

继承超类的设计

小明接到甲方爸爸订单需要设计一只鸭子,这只鸭子要求会叫,游泳,显示在屏幕中。小明想,这还不简单,采用继承的思想来做。创建一个Duck超类,让各种鸭子继承该超类。它具有叫方法quack,游泳方法swim,显示方法display,如下图所示。

Duck quack() swim() display() MarllandDuck display() RedHeadDuck display()

结果甲方爸爸提出意见:“我们需要一只会飞的鸭子。”作为一名优秀的程序员,小明表示这有什么难的。大笔一挥,在超类中加上一个fly()方法,让所有鸭子继承它就可以了。

Duck quack() swim() fly() display() MarllandDuck display() RedHeadDuck display()

但是,悲剧发生了,在会议上,甲方爸爸意外地发现一只橡皮鸭RubberDuck在飞来飞去。原来,并非所有的鸭子都会飞。小明想,这没什么。在橡皮鸭中覆盖迪掉fly()方法,改为什么都不做就好了。

Duck quack() swim() fly() display() MarllandDuck display() RedHeadDuck display() RubberDuck display() fly()

然而,问题在于,以后小明再加入一些鸭子,都要去检查fly()方法和quack()方法,并且必要时候覆盖它们。例如加入不会叫也不会飞的木头鸭子DecoyDuck,就需要将这两个方法全部覆盖。这简直就是地狱。

这里小明发现了继承提供Duck行为的问题:“牵一发而动全身,修改父类,会导致子类产生不需要的改变”

实现接口的设计

小明想了想,决定把fly()这个变化的量从超类中抽出,放进"Flyable接口",这样,只有会飞的鸭子才实现这个接口。同理,可以设计一个"Quackalbe接口",并非所有鸭子都会叫。

Duck swim() display() MarllandDuck display() RedHeadDuck display() RubberDuck display() fly() DecoyDuck display() Flyable fly() Quackable quack()

但是这样并不能解决问题,接口不能提供代码的实现,无法达到代码复用。在每个子类都必须要考虑实现Flyable和Quack中的方法。这同样让小明头疼。

采用设计模式

继承超类的设计虽然能够实现代码复用,但是不方便修改。而实现接口的设计虽然能灵活组合各种需要,但是却不能实现代码复用。
为了解决这个问题,我们需要用到第一个设计原则:

找出应用中可能需要变化之处,把他们独立出来,不要把它们和不会变化的代码混在一起

这里鸭子类的变化部分是飞行行为和呱呱叫行为(当然,有人会认为游泳也是),我们将这两个部分抽出。将这两个行为用类而不是用方法来表示。

设计鸭子的行为

为了增加鸭子行为的弹性,我们可以考虑将鸭子的行为设置为可以改变的。在鸭子类中加一个设定类setBehaviour的方法。这样就可以在运行的时候改变鸭子的行为。为了满足这个目标,我们有了第二个设计原则:

针对接口编程,而不是针对实现编程。

所谓针对接口编程,就是说,变量的声明类型应该是超类型,通常是接口,如此,只要具体实现这个超类型的对象,都可以赋值给这个变量。声明类的时候,不需要考虑真正对象的类型。也就是多态。

基于这种思想,我们设计两个接口FlyBehavior和QuackBehavior,用来作为引用变量。而它们有对应的类,负责具体的行为。

«interface» FlyBehavior fly() «interface» QuackBehavior quack() FlyWithWings fly() FlyNoWay fly() Quack quack() Squeak quack() MuteQuack quack()

整合鸭子

Duck FlyBehavior flyBehavior QuackBehavior quackBehavior performQuack() swim() display() performFly() MarllandDuck display() RedHeadDuck display() RubberDuck display() DecoyDuck display() «interface» FlyBehavior fly() «interface» QuackBehavior quack() FlyWithWings fly() FlyNoWay fly() Quack quack() Squeak quack() MuteQuack quack()

最后的代码如下

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!");
	}
}

这里我们采用了组合而不是继承的方法实现飞行和鸣叫两个动作,从而实现了较高的灵活性,不仅可以将算法封装成类,而且可以在运行的时候动态改变行为,我们得到了第三个设计原则

多用组合,少用继承

同时,恭喜你学到了第一个设计模式——策略模式

策略模式定义了算法族,分别封装起来,让它们之间可以互换,此模式让算法的变化独立于使用算法的客户。

上一篇:面向对象


下一篇:策略模式