美文网首页
给大妈说“策略模式”

给大妈说“策略模式”

作者: 番茄精 | 来源:发表于2018-01-04 19:04 被阅读0次

  我有个大胆的想法!就是给大妈来解释这个模式。为什么跟妹子一起的时候说相对论,量子力学,妹子们扭头就想走?为什么跟妹子们说杨振宁的物理建树有多高,妹子们只关心他82的时候娶了个28的姑娘?问题出在哪里?废话,你讲的一点都不生动!嗯,就是这样。从现在开始,学会做个会讲故事的程序猿!
  好了,先上本文目录。

  • 什么是“策略模式”
    • 定义
    • 结构
  • 怎么用“策略模式”
    • 为什么用“策略模式”
    • 适用场景
  • 总结

什么是“策略模式”

定义

  这么跟你解释吧。就是一首同样的广场舞的歌,不同的广场的大妈,可能有不同的舞蹈动作(我就能用健美操的动作,来跳“苍茫的天涯是我的爱”);三国时候著名的“曹冲称象”,对比宰了称或者直接称就妙多了;在比如,隔壁的马小容想去找老王的助理宋二吉去酒店打麻将,怎么去呢?戴口罩骑自行车,走路,叫滴滴blabla...都可以(为让进来的观众记忆深刻,我特地ps了一张图)。

去酒店的可选方式

  好了,作为一条咸鱼或者一根韭菜,你知道这些就够了。大妈:小伙子怎么说话呢,我不服,我就要做有梦想的咸鱼!好好好,别慌,我们来“寻梦,撑一支长蒿,向青草更青处漫溯...”。我们得把上面的2个例子抽象成具有一般性的描述,来适用和这类似的案例。我们可能经历这样的一个抽象过程:为达目的 (跳舞,去酒店打麻将...),不择手段 (这里请理解成多种手段)!或者是条条道路通罗马。咳咳,虽然有那么点意思了,但是总觉得没上升到一定高度有没有?那我们换个说话,如下:

策略模式 定义了一系列的算法,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

  这就是策略模式的定义。什么,妹子你要走???请留步!!!我再给你在讲讲日食的时候是怎么证明广义相对论的。。。好吧,不开玩笑了,这个模式的定义,仔细看看,是不是和“条条道路通罗马”,“为达目的,不择手段”有点差不多的味道???呵呵,我知道这么不生动的东西,跟大妈直接说没多大用处。所以,顶多记住下面的策略模式的三个特点完事。看看wiki的定义。其实表达的意思差不多。

In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. The strategy pattern

  • defines a family of algorithms,
  • encapsulates each algorithm, and
  • makes the algorithms interchangeable within that family.
    ——摘自 wiki

  什么,看不懂?别慌,这里有翻译。逼格一下子上去了有没有?

策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。策略模式:

  • 定义了一族算法(业务规则);
  • 封装了每个算法;
  • 这族的算法可互换代替(interchangeable)。

  好了,言归正传,我知道知识越抽象,越难以让大妈理解并接受,更关键的是,容易忘记!!!所以我还是换个说法:为达目的,不择手段(多种手段)。
  好了,到这里为止,大妈篇就过去了,知道这个概念差不多够在程序猿面前装逼了。下面程序猿篇的代码部分与应用部分,就算我有勇气跟你说,你也没勇气听。所以,我在这里加上 条三八线。




结构

  好了,现在说话就不拐弯抹角了!咳咳,哪有那么多生动的故事给你一个抠脚大汉讲?搞技术本来就是不可爱的事情。所以这里,我选择直接上图。


wiki strategy uml

  在上面左边的类图里面,Context不直接实现算法(具体行为)。而是存储了一个Stategy的引用来间接实现,这就使得Context和算法的实现分离了(解耦)。在右边的时序图里面,可以看到运行时的交互。这里在把涉及到的三个元素在总结一下:

  • 环境Context:持有一个Strategy的引用。
  • 抽象策略Strategy:定义是何种行为动作。
  • 具体的策略:不同算法实现Strategy中定义的行为动作。

  代码实现很简单,见下(From wiki中文)。这里的依赖注入采用的构造器。一般策略模式也会用setStrategy(Strategy)的方式来注入。

//StrategyExample test application

class StrategyExample {

    public static void main(String[] args) {

        Context context;

        // Three contexts following different strategies
        context = new Context(new FirstStrategy());
        context.execute();

        context = new Context(new SecondStrategy());
        context.execute();

        context = new Context(new ThirdStrategy());
        context.execute();

    }

}

// The classes that implement a concrete strategy should implement this

// The context class uses this to call the concrete strategy
interface Strategy {

    void execute();

}

// Implements the algorithm using the strategy interface
class FirstStrategy implements Strategy {

    public void execute() {
        System.out.println("Called FirstStrategy.execute()");
    }

}

class SecondStrategy implements Strategy {

    public void execute() {
        System.out.println("Called SecondStrategy.execute()");
    }

}

class ThirdStrategy implements Strategy {

    public void execute() {
        System.out.println("Called ThirdStrategy.execute()");
    }

}

// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {

    Strategy strategy;

    // Constructor
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

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

}

怎么用“策略模式”

为什么用“策略模式”

  “策略模式”用起来有什么好处?其实我的体会就是,这个模式很好的体现了OCP(开闭原则)。最直观的体现是在可拓展性上面。当然,在最后的总结部分也会总结一下好处。
  直接结合我在定义里面列举的例子来看,都是广场舞《最炫民族风》,可以用健美操二级来跳,也有一些广场的大妈可能拿着扇子来跳,也可以自定义舞蹈动作来跳...针对这种情况,一种常规的方式是将多种算法写在一个类中。如下:

class SquareDance{
  void aerobics(){...}//健美操
  void handFan(){...}//折扇
  void custom(){...}//自定义
}

  当然,也可以将这些算法封装在一个统一的方法中,通过if...else...或者case等条件判断语句来选择具体的算法。和上面这个常规的方式一样,都属于硬编码。多个算法集中在一个类的时候,类就变得臃肿,维护成本变高,最明显的就是,违反了OCP开闭原则,没有做到对修改关闭。例如这时候,北京某广场的大妈发明了一种新跳法,怎么维护进去呢?只能侵入性的去修改已经封装了的源代码了。在下面的小节里面,我拿汽车的例子来分析,更能体会到策略设计的好处。

适用场景

  直白点说,就是一个行为有多种可选实现算法的时候就可以考虑使用了。因为我不喜欢把一些规则戒律化。当然你也可以具体分析特定的场景,如果满足定义中的引用的wiki策略模式定义中的三个特点,就肯定可以用策略模式了。不过我在一本书中也看到这样描述的,这里顺手也贴一下(反正我看了是没冲动去记住,顶多理解一下,表达自己对作者的尊敬)。一定要提的是,不要为了模式而模式。人是活的。

  • 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时
  • 需要安全地封装多种同一类型的操作时。
  • 出现同一抽象类有多个子类,而又需要使用if-else或者case来选择具体子类时。

  平时生活中,你善于总结,善于发现的话,这样的场景就有很多。就像蓝猫淘气主题曲里面的“只要你爱想爱问爱动脑天地间奇妙的问题你全明了”(呸!明明是头发白的更快了)。言归正传,购物的时候,针对不同客户的不同优惠;Android动画源码中的时间插值器;开源项目UniversalImageLoader的缓存策略;现在汽车的双引擎设计以及刹车系统的不同实现等;小到生活中,钓鱼用哪个型号的钩子,配蚯蚓鱼饵还是面粉鱼饵?团建活动时,公司给了好几个地方,你选择去哪里...

  根据策略模式,一个类的行为,不应该被继承,而是应该使用接口封装起来。我用汽车来说明。简化场景,这里我们考虑汽车的两个行为:刹车和加速。既然这两个行为,在不同的车型里面差别很大,一个常规的解决方式,就是在子类里面实现这些行为。而这种方式有很明显的缺陷,我列出来:

  • 在每个子类型号中,都必须实现这两个行为。随着车型的增加,维护这两个行为的工作量非常可观。
  • 很可能在某些型号的车型里面存在重复的实现。
  • 不进子类来看代码的话,想要知道每个型号的具体行为是不容易的。

   等等这里是不是发现 《Head First设计模式》 里面第一章的讲的鸭子设计一模一样?汽车的刹车和加速,鸭子的飞和叫。。。傻子也能看出来就是一个模子刻的啊。

  而策略模式是使用组合的方式,而不是继承。在策略模式里面,不同的行为定义成不同的接口,然后指定类来实现这些接口。这就使得行为和使用这个行为的对象,更好的解耦。行为的具体实现如果改变,对使用到这个行为的类而言,不用做任何改变。而且,只要注入进不同的实现(策略)就能以很小的代价让类改变行为。行为在运行时和设计时都能改变,非常灵活。比如说,一个汽车Car对象的刹车行为可以是BrakeWithABS()和Brake()中的任何一个:

/* Encapsulated family of Algorithms
 * Interface and its implementations
 */
public interface IBrakeBehavior {
    public void brake();
}

public class BrakeWithABS implements IBrakeBehavior {
    public void brake() {
        System.out.println("Brake with ABS applied");
    }
}

public class Brake implements IBrakeBehavior {
    public void brake() {
        System.out.println("Simple Brake applied");
    }
}

/* Client that can use the algorithms above interchangeably */
public abstract class Car {
    protected IBrakeBehavior brakeBehavior;

    public void applyBrake() {
        brakeBehavior.brake();
    }

    public void setBrakeBehavior(final IBrakeBehavior brakeType) {
        this.brakeBehavior = brakeType;
    }
}

/* Client 1 uses one algorithm (Brake) in the constructor */
public class Sedan extends Car {
    public Sedan() {
        this.brakeBehavior = new Brake();
    }
}

/* Client 2 uses another algorithm (BrakeWithABS) in the constructor */
public class SUV extends Car {
    public SUV() {
        this.brakeBehavior = new BrakeWithABS();
    }
}

/* Using the Car example */
public class CarExample {
    public static void main(final String[] arguments) {
        Car sedanCar = new Sedan();
        sedanCar.applyBrake();  // This will invoke class "Brake"

        Car suvCar = new SUV();
        suvCar.applyBrake();    // This will invoke class "BrakeWithABS"

        // set brake behavior dynamically
        suvCar.setBrakeBehavior( new Brake() );
        suvCar.applyBrake();    // This will invoke class "Brake"
    }
}

总结

  策略模式主要用来分离算法,在相同的行为抽象下有不同的具体实现策略。这个模式很好的演示了开闭原则,也就是定义抽象,注入不同的实现,从而达到很好的可扩展性。

  优点

  • 结构清晰明了、使用简单直观;
  • 耦合度相对而言较低,扩展方便;
  • 操作封装也更为彻底,数据更为安全。

  缺点

  • 随着策略的增加,子类也会变得繁多;
  • 客户端必须知道所有的策略类,并自行决定具体使用哪一个策略类。

相关文章

  • 给大妈说“策略模式”

      我有个大胆的想法!就是给大妈来解释这个模式。为什么跟妹子一起的时候说相对论,量子力学,妹子们扭头就想走?为什么...

  • 给大妈说“观察者模式”

      说点题外话。昨天跟基友闲聊,聊到物理学家杨振宁,我才发现国内黑他,不了解他的人实在犹如路边的野草啊。我不得不给...

  • 策略模式

    参考资料:漫话:如何给女朋友解释什么是策略模式? 设计模式之策略模式(实例+Demo) Java设计模式(8)——...

  • 简说设计模式之策略模式

    前言:对于设计模式基础概念可以去看[简说设计模式之设计模式概述] 一、什么是策略模式 策略(Strategy)模式...

  • 三国演义之「 策略模式」

    哈喽,大家好,好久没更新干货了,今天给大家讲解设计模式中的策略模式,怕说的太枯燥,最后以三国演义的例子讲解策略模式...

  • 小白写了一堆if-else,大神实在看不下去了,直接用策略模式摆

    这里涉及到一个关键词:策略模式,那么到底什么是策略模式呢?本文就来好好给大家讲讲策略模式,大家可以带着如下几个问题...

  • c++设计模式(一)--GeekBand

    一 策略模式策略模式和 Template 模式要解决的问题是相同(类似)的,都是为了给业务逻辑(算法)具体实现和抽...

  • 11.7设计模式-策略模式-详解

    设计模式-策略模式 策略模式详解 策略模式在android中的实际运用 1.策略模式详解 2.策略模式在andro...

  • 深入浅出设计模式(一)-策略模式

    本文解决问题 什么是策略模式? 策略模式的优缺点以及策略模式解决了什么痛点 策略模式的适用环境 什么是策略模式? ...

  • 策略、工厂模式融合 InitializingBean

    策略、工厂模式融合 InitializingBean 策略、工厂模式分别是什么 策略模式 策略模式是将不同的算法封...

网友评论

      本文标题:给大妈说“策略模式”

      本文链接:https://www.haomeiwen.com/subject/oqxrnxtx.html