设计模式之策略模式

1. 介绍

策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到客户端。从概念上看,这些算法完成的功能都是一样的,只不过是具体的实现不一样,使用策略模式可以让相同的方式调用所有的算法,减少了各种算法与使用算法类之间的耦合。

策略模式类图

优点
① 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
② 简化了客户端代码,消除了多个if-else的判断;
③ 实现的选择 Strategy模式可以提供相同行为的不同实现。客户可以根据不同时间/空间权衡取舍要求从不同策略中进行选择。
缺点
① 会产生很多的策略类,每个策略都需要对应一个策略类;
② 提前需要知道有哪些策略,需要从中选择,指定最终执行的策略。
适用场景
① 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
② 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
③ 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
④ 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

2. 应用案例

现在我们需要设计一款玩具汽车,具有基本的鸣笛和行驶的功能。
玩具汽车的超类如下:

public abstract class Car {
    public Car(){
    }
    public abstract void display();
    public void whistle(){
        System.out.println("所有的汽车都会鸣笛");
    }
    public void run(){
        System.out.println("所有的汽车都会行驶");
    }
}

玩具汽车具体实现类如下:

public class ToyCar extends Car {
    @Override
    public void display() {
        System.out.println("这是玩具车!");
    }
}
public class Benz extends Car {
    @Override
    public void display() {
        System.out.println("这是私家车!");
    }
}

其他实现省略。

需求变动:玩具车需要具有飞行功能

简单的修改

在超类中新增fly方法,让所有的汽车都具备飞行的能力

public abstract class Car {
    //其他方法省略
    public void fly(){
        System.out.println("所有的汽车都会飞");
    }
}

产生的问题:绝大部分汽车本身不能飞,但是在Car类中添加了fly方法,却错误的赋予了它们飞行的能力,比如,奔驰车Benz,超类中新加的方法,会影响所有子类的行为。

简单的解决办法:最简单的解决办法就是利用方法的复写@Override,复写fly方法,根据不同汽车的特性去实现不同的fly。(缺陷:每新加一种汽车,可能就会去复写超类的方法,如,加入一只木制汽车,它可能既不会飞行,又不能发出声音,而且随着种类越多,那么复写的次数越多,重复的代码也就会越多,后期维护的时候可能就需要同时改很多个地方)

设计原则:应用中可能会产生变化之处,要把它们独立出来,不要和不需要变化的代码放在一处(不变化的代码一般是稳定的,加入变化的代码之后很可能就会破坏原来的稳定性)

独立变化的修改

变化的部分:飞行行为,鸣笛行为

//飞行行为
public interface FlyBehavior {
    void fly();
}
//鸣笛行为
public interface WhistleBehavior {
    void whistle();
}

Car超类修改

public abstract class Car {

    private FlyBehavior flyBehavior;
    private WhistleBehavior whistleBehavior;
    public abstract void display();

    public Duck(FlyBehavior flyBehavior, WhistleBehavior whistleBehavior) {
        this.flyBehavior = flyBehavior;
        this.whistleBehavior = whistleBehavior;
    }
    public void fly() {
        flyBehavior.fly();
    }
    public void whistle() {
        whistleBehavior.whistle();
    }
    public void run() {
        System.out.println("所有汽车都会行驶");
    }
    // 加入set方法后就可以随时调用这两个方法来改变汽车的行为(策略模式的精髓,算法之间的相互替换)
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
    public void setWhistleBehavior(WhistleBehavior whistleBehavior) {
        this.whistleBehavior = whistleBehavior;
    }
}

Fly接口的实现类

  1. 不会飞行:
public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("我无法飞行");
    }
}
  1. 可以飞行
public class FlyWithPower implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("我能够飞行");
    }
}

鸣笛行为实现类

  1. 不会鸣笛
public class MuteWhistle implements WhistleBehavior {
    @Override
    public void whistle() {
        System.out.println("无法鸣笛");
    }
}
  1. 可以鸣笛
public class Whistle implements WhistleBehavior {
    @Override
    public void whistle() {
        System.out.println("滴滴滴~");
    }
}

具体的汽车类(玩具汽车)

public class ToyCar extends Car {

    //在超类中有setFlyBehavior,setQuakBehavior,同样允许客户端自己选择
    public ToyCar() {
        super(new FlyWhithPower(), new Whistle());
    }
    @Override
    public void display() {
        System.out.println("这是一辆玩具汽车");
    }
}

现在每辆汽车的鸣笛和飞行都可以自己选择灵活配置,不仅可以切换汽车,还可以切换汽车的行为,这在原来的设计上是办不到的,现在加一种新的汽车变得很简单了,而且不会对原来的设计造成影响。

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

推荐阅读更多精彩内容

  • 一直想把常见的设计模式系统地学习一遍,结果和大多数人一样,过了几天就没能坚持下去了。我发现学习这件事情急不得,往往...
    Neulana阅读 565评论 5 2
  • 策略模式,是我们接触到的第一个设计模式,也是较容易理解的一个模式。我们可以给它下一个定义:** 定义了算法族,分别...
    六尺帐篷阅读 717评论 0 8
  • 策略模式定义算法族 , 分别封装起来,从而使得它们可以相互替换。策略模式使得算法的变化独立于使用算法的客户端。 分...
    在下喵星人阅读 369评论 3 1
  • 策略模式 大多数问题都可以使用多种方法来解决。以排序问题为例,对于以一定次序把元素放入一个列表,排序算法有很多。通...
    英武阅读 1,828评论 0 50
  • 今天的风像画了淡妆的空姐,清淡优雅,人人感到舒畅 而我的心口,有些不顺畅,有几丝心慌,几缕惆怅,动力有些不足,不知...
    柠檬珍阅读 265评论 0 4