浅入浅出设计模式

 

简介                                                                                  

GoF(“四人帮”Gang of Four,指Erich Gamma, Richard Helm, Ralph Johnson & John Vlissides四人)的《设计模式》(1995年出版)是第一次将设计模式提升到理论高度,并将之规范化。本书提出了23种基本设计模式,自此,在可复用面向对象软件的发展过程中,新的大量的设计模式不断出现。

 

1.从前有一群鸭子                                                               

在遥远的丹麦生活着一群快乐的鸭子,它们会唱歌(quack),跳舞(swim)还会表现自己(display)。我们来把这群鸭子设计成一个类。UML图如下所示

就像我们看到的,每只鸭子都会同样的行为。

 

2.有鸭自远方来                                                                  

后来的一天,从非洲来了一群“南非鸭”,它们和丹麦鸭一样会唱歌跳舞,不过表现自己就和丹麦鸭的表现不同了。

那这里我们就来用抽象类的方式来解决这个问题,如下图所示(抽象用斜体表示)

这样,我们通过一个抽象类Duck来实现quack和swim方法的复用,而不同的地方display由继承的子类分别实现。

要点1:找出应用中可能变化之处,把它们独立出来,不要和那些不需要变化的代码混合在一起。

 

3.每只鸭子上辈子都是折翼的天使                                         

那是一个电闪雷鸣的日子,鸭老大给小鸭们讲了它们祖先天鹅翱翔于蓝天的故事。原来,它们上辈子都是折翼的天使……

于是,这群鸭子展开翅膀~~  灰了起来 - -||

到了这里,鸭子们需要一个飞(fly)的方法,既然大家都会飞,就加到父类Duck里吧。

 

但是,有的鸭子(玩具鸭,姑且算鸭子吧)不会飞啊~~  那该怎么办呢?我们的设计是不是出现了错误?

嗯~ 我们先覆盖父类中的方法来实现我们的需求吧。

OK,我们实现了需求

要点2:为了"复用"(reuse)目的而使用继承,貌似不是很好

 

仔细思考:

1、我们是不是把这个问题解决了?

2、如果又来了个A鸭子(不会叫,不会有用,不会飞);或者是B鸭(会汪汪叫,会游泳,不会飞)等等,我们该怎么办?

3、利用继承来提供Duck的行为,这会导致下列哪些缺点?
 a.代码在多个子类中重复
 b.运行时的行为不容易改变
 c.我们不能让鸭子跳舞
 d.很难知道所有鸭子的全部行为
 e.鸭子不恩那个同时又飞又叫
 f.会造成其他鸭子不想要的改变

 

4.你是否想到了"接口"                                                        

看起来不错的样子~

如果子鸭子类特别多,问题就出来了,那岂不是每种鸭子都各自实现?无法实现代码复用的目的?

1、现在我们知道使用继承并不能很好的解决问题,因为鸭子的行为在子类里不断的改变,并且让所有的子类都有这些行为是不恰当的。

2、Flyable和Quackable接口一开始似乎不错,解决了问题(只有会飞的鸭子才继承Flyable),但是接口不具有实现代码,所以继承接口无法达到代码的复用。

要点3:多用组合,少用继承

 

5.重新设计鸭子                                                              

Ⅰ.分开变化和不会变化的部分

(本图截取自深入浅出设计模式

 

Ⅱ.针对接口编程 (题外话)

我们利用接口代表每个行为,比方说IFlyBehavior和IQuackBehaviour,而行为的每个实现都将实现其中的一个接口。

这种方法和以往不同的地方在于,以前的做法是:行为来自Duck父类的具体实现,或者是继承某个接口并由某个接口来自行实现而来。这两种做法都是依赖于“实现”, 我们被实现绑住,没办 法更改行为(除非写更 多的代码)

举例说明:

"针对实现编程"
Dog d=new Dog();
d.bark();

"针对接口/超类型编程"
Animal animal=new Dog();
animal.bark();

更好的是,子类实例化的动作不再需要在代码中硬编码,例如
new Dog();而是"在运行时才指定具体实现的对象"
a=getAnimal();
a.makeSound();

 

Ⅲ.实现鸭子的行为

 

Ⅳ. 整合鸭子的行为

注意,上图中的Duck的两个成员是接口类型,即我们上面提到的IFlyBehavior 和IQuackBehavior 接口类型

 

      

别忘记,因为yellowduck继承了duck的,所以具有flyBehavior和quackBehavior实例变量

 

Ⅴ. 测试代码

输入并编译测试类

 

public class MiniDuck()
{
	public static void main(string[] args)
	{
		Duck mini=new YellowDuck();
		mini.performFly();
		mini.performQuack();
	}
}


⑤运行代码

 

6.总结                                                              

上图是整个重新设计后的类图

我们这里描述事情的方式也少有改变。不再把鸭子的行为说成是“一组行为”。我们开始把行为想成是“一组算法”。

而上图就是GoF的设计模式中的策略模式

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