你好呀,我是Bella酱~
今天我们来一起聊聊策略模式,主要是以下几个方面:定义、生活中的例子、核心组成、UML图、代码实现、适用场景等。
定义
什么是策略模式呢?
引用GoF著作的《设计模式:可复用面向对象软件的基础》中的一段话:
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
什么意思呢?就是说有N个算法,它们要实现的目的是相同的,所以各算法之间是可以互相替换的,每个客户端独立选择使用哪一个算法,不同的客户端之间可以选择不同的算法。
生活中的例子
其实在我们的日常生活中就有很多使用策略模式的例子呢。
例如大多数工作日我们都要做的一个事情,使用某一种交通方式从家去公司。具体使用哪种交通方式呢?根据家到公司的距离,不同的同学会选择不同的交通方式,例如搭乘地铁、自驾、骑行、步行等等。
无论是哪种交通方式,我们的目的是相同的--从家去公司。各种交通方式是独立的,而且是可以互相替换的。每一位同学又会根据个人的实际情况来选择不同的交通方式。这不就是典型的策略模式的例子吗?
核心组成
策略模式主要由4部分组成:
- Strategy策略接口。该接口定义了一个方法,各具体策略类会实现该方法。
- ConcreteStrategy具体策略。ConcreteStrategy有很多个,它们都实现了Strategy策略接口中定义的方法。
- Context上下文。Context中定义了策略类,并且定义了一个方法,该方法在执行时会执行各具体策略中的方法。
- Client客户端。Client客户端中会创建Context上下文,并且指定具体策略类,然后再执行Strategy策略接口中定义的方法。
以上文中的例子为例:
- Strategy策略接口中定义了一个方法:选择从家去公司的交通方式。
- ConcreteStrategy具体策略,例如地铁策略、自驾策略、骑行策略等等,均实现了“选择从家去公司的交通方式”这一方法,但是各具体策略之间的方法体是不同的。
- Context上下文中定义了Strategy策略类,同时Context上下文中还有一个方法,该方法会执行Strategy策略接口中定义的方法。
- Client客户端中创建Context上下文,并且指定用哪种具体策略,例如指定使用骑行策略,然后调用Context中定义的方法来执行策略中的方法。
UML图
以上文中的例子为例,策略模式的UML图如下:
对UML类图有疑惑的同学可以看下这篇文章:看懂UML图
代码实现
我们来一起看下如何用代码实现上述例子吧,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,结果如下:
诺,就是我们想要的效果啦!
适用场景
通过上面的讲解,相信你已经掌握策略模式的使用啦,但是我们应该什么时候使用策略模式呢?
- 当一个行为因不同的条件要执行不同的方法体时。其实就是我们平常见到的if else啦。对于项目中的if else语句,可以考虑下是否可以定义不同的具体策略类,然后将else中的语句移入到各个ConcreteStrategy中哦!
- 当许多相似的类,仅仅是行为上有差异时。这些相似的类是不是就像各个不同的ConcreteStrategy呢?把那些相似的类利用策略模式重构一波后,代码结构会更加清晰,可读性也会提高很多!
- 当对于一个算法,已有几种不同的实现方式,而且后续还可能会增加新的实现方式时。用策略模式,可以在你要新增算法的实现方式时,不修改已有算法的实现,大大降低了引入新策略的风险。
好啦,我们今天的文章就到这里啦,文中涉及的代码都可以从 https://github.com/lilinru/design-patterns 这里下载哦,我们下期见~