设计模式2:策略模式

今天来讲一讲“策略模式”(Strategy Pattern)。为什么第二个讲它呢?单例模式第一个讲是我开始以为它最简单(其实并不是),策略模式第二个讲是因为《Head First Design Patterns》这本书第一个讲的就是策略模式。

什么是策略模式?

策略模式就是定义算法族,把它们封装起来,让它们可相互替换,在用的时候再决定用哪一个算法(原文:Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it)。
这个定义可能比较难以理解。以我的理解,所谓策略,就是随机应变,根据具体情况作出最佳选择。
这样做的好处是什么?
最大的好处就是代码灵活。按照《Head First Design Patterns》所说,要把不变的东西保留复用,变的东西抽出来。策略模式中,父类只负责调用接口函数,而不关心具体是怎么实现的,并且利用多态可以自由选择实现接口类。

代码例子

定义一个屠龙勇士对象,应用屠龙接口,这个接口有三个候选策略:近战,远程和法术。
屠龙接口:

@FunctionalInterface
public interface DragonSlayingStrategy {

  void execute();

}

屠龙勇士类:

public class DragonSlayer {

  private DragonSlayingStrategy strategy;

  public DragonSlayer(DragonSlayingStrategy strategy) {
    this.strategy = strategy;
  }

  public void changeStrategy(DragonSlayingStrategy strategy) {
    this.strategy = strategy;
  }

  public void goToBattle() {
    strategy.execute();
  }
}

近战接口实现类(其余两个类似就省略了):

public class MeleeStrategy implements DragonSlayingStrategy {

  private static final Logger LOGGER = LoggerFactory.getLogger(MeleeStrategy.class);

  @Override
  public void execute() {
    LOGGER.info("With your Excalibur you sever the dragon's head!");
  }
}

测试:

public static void main(String[] args) {
    // GoF Strategy pattern
    LOGGER.info("Green dragon spotted ahead!");
    DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy());
    dragonSlayer.goToBattle();
    LOGGER.info("Red dragon emerges.");
    dragonSlayer.changeStrategy(new ProjectileStrategy());
    dragonSlayer.goToBattle();
    LOGGER.info("Black dragon lands before you.");
    dragonSlayer.changeStrategy(new SpellStrategy());
    dragonSlayer.goToBattle();

    // Java 8 Strategy pattern
    LOGGER.info("Green dragon spotted ahead!");
    dragonSlayer = new DragonSlayer(
        () -> LOGGER.info("With your Excalibur you severe the dragon's head!"));
    dragonSlayer.goToBattle();
    LOGGER.info("Red dragon emerges.");
    dragonSlayer.changeStrategy(() -> LOGGER.info(
        "You shoot the dragon with the magical crossbow and it falls dead on the ground!"));
    dragonSlayer.goToBattle();
    LOGGER.info("Black dragon lands before you.");
    dragonSlayer.changeStrategy(() -> LOGGER.info(
        "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"));
    dragonSlayer.goToBattle();
  }
}

前半部分使用的是之前定义的三个实现类,后半部分使用lambda表达式,其实相当于:

dragonSlayer.changeStrategy(new DragonSlayingStrategy() {
    @Override
    public void execute(){/* 实现   */}
});

是不是很熟悉?对了,安卓里面的OnClick方法不就是这样吗?只不过貌似没有预先定义的点击事件。
也就是说,除了可以在预先定义好的策略里面选择,还可以临时创建一个新的匿名内部类来实现接口。

总结

很多时候,代码的修改和维护占的比重大于代码的初创,因此考虑代码存在的改变可能而事先规划好,某种程度上是能力到一定层次的体现。
策略模式假如使用得当,可以大大提升代码的灵活性。应该好好想一想,哪些代码是在做一个本质的不同方法实现?原来的实现是不是就是把所有方法堆在一起?可不可以抽象出来做一个接口,然后用策略模式来解决?
当代码简单的时候,可能看不出来特别的好处。比如上面的例子,我们当然也可以直接在勇士类里面加入三种屠龙方法。那么好,第四种方法来了呢?再加上。假设现在要有一个法师类,除了只会用法术屠龙以外,别的都和勇士差不多,怎么办?这时候弊端就体现出来了:因为法师不会近战,因此不能继承勇士,代码无法复用。
你可能会说:继承啊没关系,其他方法不用就行了。但这就会造成隐患,因为你就无法阻止别人调用其他三种方法,就可能会造成错误。这时代码就显得不优雅。

参考

https://github.com/iluwatar/java-design-patterns/tree/master/strategy

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

推荐阅读更多精彩内容

  • 1 场景问题# 1.1 报价管理## 向客户报价,对于销售部门的人来讲,这是一个非常重大、非常复杂的问题,对不同的...
    七寸知架构阅读 5,031评论 9 62
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 1 场景问题 1.1 报价管理 向客户报价,对于销售部门的人来讲,这是一个非常重大、非常复杂的问题,对不同的客户要...
    4e70992f13e7阅读 3,070评论 2 16
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,897评论 1 15
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,717评论 2 17