设计模式入门

有些人已经解决了你的问题

  • 经验复用

 设计模式是利用其他开发人员的经验和智慧;

  • 使用方式

 把模式装进脑子里,寻找如何使用它们;

先从简单的模拟鸭子应用做起

需求:

  • 设计一套模拟鸭子的游戏:

游戏中有各种鸭子,一边游泳戏水,一边呱呱叫;

  • 如何使用标准OO技术实现?

设计一个超类,并让各种鸭子继承此超类;

设计一个超类

现在我们得让鸭子能飞

  • 变动后的需求如何实现?

在 Duck 类中增加一个 fly() 方法

 

 

但是,可怕的问题发生了...

  • 事实是

并非Duck 所有的子类都会飞,比如橡皮鸭

对代码所做的局部修改,影响层面可不是局部(会飞的橡皮鸭子)

为了“复用”目的使用继承,结局并不完美

橡皮鸭子类(Rubber Duck)

 

通过继承解决?

 

 

利用继承提供Duck行为的缺点

  • 缺点:

代码在多个子类中重复

运行时的行为不容易改变 很难知道所有鸭子的全部行为

改变会牵一发动全身,造成其他鸭子不想要的改变

  • 后果:

每当有新的鸭子子类出现,都要被迫检查并可能需要覆盖 fly() 和 quark()

利用接口如何?

 

利用接口优缺点

  • 解决的问题

解决了 “并非所有的子类都具有飞行和呱呱叫的行为”

  • 带来的问题

造成代码无法复用;

当所有子类都要稍微修改一下飞行的行为时工作量巨大

软件开发的一个不变真理

  • 不变真理:

变化

不管当初软件设;计得多好,一段时间后总是需要成长与改变;

把问题归零......

总结:

  • 使用继承并不能很好的解决问题

1)鸭子的行为在子类里不断地改变;

2)让所有的子类都有这些行为是不恰当的;

使用接口也不能很好的解决问题

  • 接口不具有实现代码,所以继承接口无法达到代码复用;

这意味着:

无论何时修改某个行为,都要往下追踪并在每一个定义侧行为的类中修改它, 容易造成新的错误。

第一个设计原则

设计原则:

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

解读:

把会变化的部分取出并 “封装” 起来,好让其他部分不会受到影响。

影响:

代码变化引起的不经意后果变少,系统变得更有弹性。

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

Duck 类中变化的部分:

fly() 和 quack()

如何分?

建立两组类,一个是 “fly” 相关,一个是 “quack” 相关;

为了要把这两个行为从 Duck 类中分开,我们将把它们从 Duck 类 中取出来,建立两组新类来代表每个行为;

把行为从Duck类中分开

 

 如何设计鸭子的行为?

要求:

1、一切能有弹性;

2、能够“指定”行为到鸭子的实例,换句话说: 在鸭子类中包含设定行为的方法, 这样就可以在“运行时”动态地“改变”鸭子的行为

第二个设计原则

设计原则:

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

解读:

针对实现编程:

1)行为来自 Duck 超类的具体实现,

2)继承某个接口并由子类自行实现而来;

注: 这两种做法都是依赖于“实现”,我们被实现绑得死死的, 没办法更改行为(除非写更多代码)

针对接口编程:

1)利用接口代表每个行为(如:FlyBehavior 与 QuackBehavior ), 而行为的每个实现都将实现其中的一个接口;

2)由行为类而不是 Duck 类来实现行为接口;

影响:

新设计中,鸭子的子类将使用接口(FlyBehavior 与 QuackBeahvior) 所表示的行为,所以实际的“实现”不会绑死在鸭子的子类中。

实现鸭子的行为

 

整合鸭子的行为

关键:

鸭子现在会将飞行和呱呱叫的动作“委托”被人处理, 而不是使用定义在Duck类(或子类)内的呱呱叫和飞行方法。

做法:

1)在鸭子类中加入两个示例变量。

2)实现 performQuack();

 

 3)设定 flyBehavior 与 quackBehavior 的实例变量

 

 Duck 类 

 

FlyBehavior 接口及行为类

 

 QuackBehavior 接口及实现类

 

MallardDuck 类

 

 客户端代码

 

 

 

posted on 2020-10-21 18:16  switch大咖  阅读(149)  评论(0编辑  收藏  举报