设计模式入门 -- 策略模式

模拟鸭子游戏的需求

SimUDuck
游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。
通过标准的OO技术,设计一个超类。

策略模式01

需求增加

此程序需要会飞 的鸭子竞争者抛在后头

方法一:在 Duck 超类加上 fly() 方法

但问题发生了:并非 Duck 所有的子类都会飞(玩具鸭子)

注意:当涉及到维护是,为了“复用”(reuse)目的而使用继承,结局并不完美

方法二:覆盖超类的方法

当有新的鸭子出现,要检查每个鸭子可能要覆盖 fly() 和 quark() ...

  • 利用继承来提供行为,导致的缺点
    1. 代码在多个子类中重复
    2. 运行时的行为不容易改变
    3. 很难知道所有鸭子的全部行为
    4. 改变引发全身,造成其他鸭子不想要的改变

方法三:利用接口

可以把 fly() 从超类中提取出来,放进一个“Flyable接口”中。这么一来,只有会飞的鸭子才实现此接口。
看似不错(只适应用不多,且飞的方式不同的情况),但是在会飞的鸭子种类很多后,要稍微修改一下飞行的行为,就是一个灾难...

分析

  1. 不是所有的子类都具有飞行和呱呱叫的行为,所以继承不合适。
  2. 虽然 Flyable 和 Quackable 可以解决“一部分”问题,但是却造成代码无法复用,这只是从一个坑进入另一个坑。
  3. 甚至,在会飞的鸭子中,飞行动作可能还有多种变化。。。

解决之道--“采用良好的OO软件设计原则”

软件开发的一个不变的真理 --> 需求和改变

第一个设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
把会变化的部分取出来并“封装”起来,好让其它部分不会受到影响

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

设计鸭子的行为

让鸭子类中的行为可以动态的改变就好了。我们应该在鸭子类中包含设定行为的方法,这样就可以在“运行时”动态的“改变”飞行行为。

第二个设计原则:针对接口编程,而不是针对实现编程
针对超类型编程

利用接口代表每个行为,比方说 FiyBehavior 和 QuackBehavior ,而行为的每个实现都将实现其中的一个接口。由行为类而不是Duck类来实现接口。


  • 以前的做法:行为来自 Duck 超类的具体实现,或是继承某个接口并由子类自行实现而来。都依赖于“实现”,被实现绑的死死的,没办法更改行为(除非写更多代码)。

vs

  • 新设计中:鸭子的子类将使用接口(FlyBehavior 与 QuackBehavior)所标示的行为,实际的“实现”不会被绑死在鸭子的子类中。即特定的具体行为编写在实现了FlyBehavior 与 QuackBehavior 的子类中。

策略模式02

这样的设计,可以放飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子类无关了。
而我们可以新增一些行为,不会影响到既有的行为类,也不会影响“使用”到飞行行为的鸭子类。

整合鸭子的行为

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

  1. 首先,在 Duck 类中加入“两个实例变量”,分别为“flyBehavior”与“quackBehavior”,声明为接口类型(而不是具体类实现类型),每个鸭子的对象都会动态地设置这些变量以在运行时引用正确的行为类型。
策略模式02
  1. 实现 performQuack();
public class Duck{
    QuackBehavior quackBehavior;
    // 还有更多
    public void performQuack(){
        quackBehavior.quack();//鸭子对象不亲自处理呱呱叫行为,而是委托给quackBehavior引用的对象。
    }
}
  1. 如何设定 flyBehavior 与 quackBehavior的实例变量
public class MallardDuck extends Duck{
    public MallardDuck(){
        quackBehavior = new  Quack();
        flyBehavior = new FlyWithWings();
    }
    //别忘了,因为 MallarDuck 继承了 Duck 类,所以具有 flyBehavior 与 quackBehavior 实例变量
}

我们正在构造器里面制造了具体的Quack实现类的实例(在后面的模式中,可以修正这一点)

动态设定行为

通过 set 方法来设定鸭子的行为,而不是在鸭子的构造器内实例化。
Duck 类中 加入 setFlyBehavior(FlyBehavior fly);

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容