一文聊透策略模式

304 阅读6分钟

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

你好呀,我是Bella酱~

今天我们来一起聊聊策略模式,主要是以下几个方面:定义、生活中的例子、核心组成、UML图、代码实现、适用场景等。

定义

什么是策略模式呢?

引用GoF著作的《设计模式:可复用面向对象软件的基础》中的一段话:

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

什么意思呢?就是说有N个算法,它们要实现的目的是相同的,所以各算法之间是可以互相替换的,每个客户端独立选择使用哪一个算法,不同的客户端之间可以选择不同的算法。

生活中的例子

其实在我们的日常生活中就有很多使用策略模式的例子呢。

例如大多数工作日我们都要做的一个事情,使用某一种交通方式从家去公司。具体使用哪种交通方式呢?根据家到公司的距离,不同的同学会选择不同的交通方式,例如搭乘地铁、自驾、骑行、步行等等。

无论是哪种交通方式,我们的目的是相同的--从家去公司。各种交通方式是独立的,而且是可以互相替换的。每一位同学又会根据个人的实际情况来选择不同的交通方式。这不就是典型的策略模式的例子吗?

核心组成

策略模式主要由4部分组成:

  1. Strategy策略接口。该接口定义了一个方法,各具体策略类会实现该方法。
  2. ConcreteStrategy具体策略。ConcreteStrategy有很多个,它们都实现了Strategy策略接口中定义的方法。
  3. Context上下文。Context中定义了策略类,并且定义了一个方法,该方法在执行时会执行各具体策略中的方法。
  4. Client客户端。Client客户端中会创建Context上下文,并且指定具体策略类,然后再执行Strategy策略接口中定义的方法。

以上文中的例子为例:

  1. Strategy策略接口中定义了一个方法:选择从家去公司的交通方式。
  2. ConcreteStrategy具体策略,例如地铁策略、自驾策略、骑行策略等等,均实现了“选择从家去公司的交通方式”这一方法,但是各具体策略之间的方法体是不同的。
  3. Context上下文中定义了Strategy策略类,同时Context上下文中还有一个方法,该方法会执行Strategy策略接口中定义的方法。
  4. Client客户端中创建Context上下文,并且指定用哪种具体策略,例如指定使用骑行策略,然后调用Context中定义的方法来执行策略中的方法。

UML图

以上文中的例子为例,策略模式的UML图如下:

对UML类图有疑惑的同学可以看下这篇文章:看懂UML类图

策略模式UML图.jpg

代码实现

我们来一起看下如何用代码实现上述例子吧,so cool~

首先定义一个Strategy策略接口。

package com.bella.design.patterns.strategy;
​
/**
 * 策略类
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public interface Strategy {
    /**
     * 选择从家去公司的交通方式
     *
     * @param home    家
     * @param company 公司
     * @return 交通方式
     */
    String selectTransportation(String home, String company);
}
​

其次定义3个具体策略,分别为SubwayStrategy、CarStrategy、BikeStrategy。 SubwayStrategy地铁策略。

package com.bella.design.patterns.strategy;
​
import java.text.MessageFormat;
​
/**
 * 地铁策略
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class SubwayStrategy implements Strategy {
​
    /**
     * 选择从家去公司的交通方式
     *
     * @param home    家
     * @param company 公司
     * @return 交通方式
     */
    public String selectTransportation(String home, String company) {
        System.out.println(
                MessageFormat.format("家在-{0}, 公司在-{1},建议您乘坐地铁出行!", home, company));
        return "Subway";
    }
}
​

CarStrategy自驾策略。

package com.bella.design.patterns.strategy;
​
import java.text.MessageFormat;
​
/**
 * 自驾策略
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class CarStrategy implements Strategy {
    /**
     * 选择从家去公司的交通方式
     *
     * @param home    家
     * @param company 公司
     * @return 交通方式
     */
    public String selectTransportation(String home, String company) {
        System.out.println(
                MessageFormat.format("家在-{0}, 公司在-{1},安全带已系好,秋名山车神准备出发啦!", home, company));
        return "Car";
    }
}
​

BikeStrategy骑行策略。

package com.bella.design.patterns.strategy;
​
import java.text.MessageFormat;
​
/**
 * 骑行策略
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class BikeStrategy implements Strategy {
    /**
     * 选择从家去公司的交通方式
     *
     * @param home    家
     * @param company 公司
     * @return 交通方式
     */
    public String selectTransportation(String home, String company) {
        System.out.println(
                MessageFormat.format("家在-{0}, 公司在-{1},绿色出行,为环保贡献一份力量!", home, company));
        return "Bike";
    }
}
​

然后再定义Context上下文。

package com.bella.design.patterns.strategy;
​
/**
 * 上下文
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class Context {
    /**
     * 策略
     */
    private Strategy strategy;
​
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
​
    /**
     * 选择去公司的交通方式
     *
     * @param home
     * @param company
     * @return
     */
    public String selectTransToCompany(String home, String company) {
        return strategy.selectTransportation(home, company);
    }
}
​

最后再来一个测试类测下效果~

package com.bella.design.patterns.strategy;
​
/**
 * 策略模式Test
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class StrategyTest {
    public static void main(String[] args) {
        Context strategyContext = new Context(new SubwayStrategy());
        String trans = strategyContext.selectTransToCompany("京杭大运河", "未来科技城");
        System.out.println(trans);
​
        strategyContext = new Context(new CarStrategy());
        trans = strategyContext.selectTransToCompany("老余杭", "未来科技城");
        System.out.println(trans);
​
        strategyContext = new Context(new BikeStrategy());
        trans = strategyContext.selectTransToCompany("未来科技城", "未来科技城");
        System.out.println(trans);
    }
}
​

run一下StrategyTest,结果如下:

StrategyTest运行结果.jpg

诺,就是我们想要的效果啦!

适用场景

通过上面的讲解,相信你已经掌握策略模式的使用啦,但是我们应该什么时候使用策略模式呢?

  1. 当一个行为因不同的条件要执行不同的方法体时。其实就是我们平常见到的if else啦。对于项目中的if else语句,可以考虑下是否可以定义不同的具体策略类,然后将else中的语句移入到各个ConcreteStrategy中哦!
  2. 当许多相似的类,仅仅是行为上有差异时。这些相似的类是不是就像各个不同的ConcreteStrategy呢?把那些相似的类利用策略模式重构一波后,代码结构会更加清晰,可读性也会提高很多!
  3. 当对于一个算法,已有几种不同的实现方式,而且后续还可能会增加新的实现方式时。用策略模式,可以在你要新增算法的实现方式时,不修改已有算法的实现,大大降低了引入新策略的风险。

好啦,我们今天的文章就到这里啦,文中涉及的代码都可以从 github.com/lilinru/des… 这里下载哦,我们下期见~