[head first 设计模式] 第一章 策略模式

[head first 设计模式] 第一章 策略模式

让我们先从一个简单的鸭子模拟器开始讲起。

假设有个简单的鸭子模拟器,游戏中会出现各种鸭子,此系统的原始设计如下,设计了一个鸭子超类,并让各种鸭子继承此超类。

1.jpg

若此时我们有了一个新的需求,我们需要鸭子会飞,那么我们该如何修改代码呢?

最初,我们想在基类上加上fly方法,使得所有子类鸭子都拥有相应的fly方法。但这样错误产生了,即使是本不该会飞的橡皮鸭子也拥有了fly方法。

或许我们可以把橡皮鸭中的fly方法覆盖掉,但这样每次新加入的不会飞的新鸭子类型,难道都要额外覆盖一次fly方法吗?太麻烦了。

利用继承来提供duck的行为,会导致运行时的行为不容易改变,且改变容易牵一发动全身。

那么,利用接口如何?

若经常需要更新产品,那么每次覆盖fly简直是噩梦。那么,我们将fly单独写成一个接口,只有会飞的鸭子实现这个接口如何?

2.jpg

但这样其实重复的代码会变得非常多,造成fly代码无法复用,每个会飞的鸭子都要实现fly方法。

那么我们该如何解决这个问题?在使用设计模式之前,不妨先求索于OO原则!

软件开发中,什么是永恒真理?

唯一不变的是变化本身——约翰逊·斯宾塞

现在我们已经知道了继承无法很好的解决问题,因为鸭子的行为在子类中不断改变,并且有的行为子类不应该拥有。使用接口初看挺不错的,但继承接口无法达到代码的复用。这意味着,无论合适你需要修改某个行为,你必须向下追踪并在每一个定义此行为的类中修改它。

但还好,有一个OO设计原则正好适用于此种情况:

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

也就是把会变化的部分取出来,好让其他部分不会受此影响。

把会变化的部分取出来并封装,以后可以轻易地改动或扩充此部分,而不影响其他不需要变化的部分。

那么,现在是时候把鸭子的行为从Duck类中取出了。

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

目前而言,除了fly()和quack()以外,duck类其他部分看起来不怎么变动,所以我们仅做些小改变。

为此,我们准备建立两组类,一个是和fly相关的,另一个和quack相关的,每一组类都实现各自的动作。

3.jpg

设计鸭子的行为

如何设计

那组实现飞行和叫声的类呢?我们希望一切能有弹性,并且能够将行为指定到鸭子的实例。并且可以让鸭子的行为动态的改变。

有了这些目标要实现,我们看第二个设计原则

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

从现在开始,鸭子的行为将被放在分开的类中,此类专门提供某行为接口的实现。这样,鸭子类就不再需要知道行为的具体细节。

这次鸭子类不会负责实现flying和quacking接口,而是由我们制造一组其他类专门实现flybehavior和quackbeavior,这就称为行为类,由行为类而不是duck类来实现行为接口。

这种做法和以往不同,以往是行为来自于duck超类的具体实现,或是继承某个接口并由子类自行实现而来。这两种方法都是依赖于实现,没法变更行为。

在我们的新设计中,鸭子的子类将使用接口所表示的行为,所以具体的实现不会被绑定在鸭子的子类中。

4.jpg

整合鸭子的行为

关键在于,鸭子会将飞行和叫声行为委托给其他对象处理。而不是由自己定义。

在Duck类中加入flyBehavior和quackBehavior变量,声明为接口类型。每个鸭子对象动态设置这些变量以在运行时引用正确的行为类型。

代码如下

public interface FlyBehavior {
    public void fly();
}
public interface QuackBehavior {
    public void quack();
}
public class FlyWithWings implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("I'm flying");
    }
}
public class FlyNoWay implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("I can't fly");
    }
}
public class Quack implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("quack!");
    }
}
public class MuteQuack implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("<<silence>>");
    }
}
public class Squeak implements QuackBehavior{

    @Override
    public void quack() {
        System.out.println("Squeak!");
    }
}
public abstract class Duck {
    protected FlyBehavior flyBehavior;
    protected  QuackBehavior quackBehavior;
    abstract void display();
    public void performFly()
    {
        flyBehavior.fly();
    }
    public void performQuack()
    {
        quackBehavior.quack();
    }
}
public class MallardDuck extends Duck{
    @Override
    public void display() {
        System.out.println("I'm a real mallard duck");
    }
    public MallardDuck(){
        flyBehavior = new FlyWithWings();
        quackBehavior  = new Quack();
    }
}
public class MiniDuckSimulator {
    public static void main(String[] args) {
        Duck mallardDuck = new MallardDuck();
        mallardDuck.performFly();
        mallardDuck.performQuack();
    }
}

动态设定行为

在鸭子子类中为两个behavior加入set方法,而不是在构造器中进行实例化。有了这个,我们就能在运行时随时改变鸭子的行为。


    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }

整体设计

现在我们来看看整体结构

5.jpg

我们不再把鸭子的行为说成是行为,而是一族算法。算法代表鸭子能做的事情。在本例中,我们鸭子的行为是组合来的,而不是继承来的。

我们得到第三个OO设计原则

多用组合,少用继承

学习完以上部分,我们正式定义策略模式

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

推荐阅读更多精彩内容