背景
在项目开发中实现某一个功能有多种可能性,根据条件的不同选择不同的方式来完成该功能。最常见的就是所有代码写在一个类中,该类中提供多个方法去实现不同条件下的功能或者干脆就是全部写在一个方法中,通过 if … else 或者 case 等条件来判断进行选择。不管怎么样,都不是太美观,代码臃肿可读性太差,后期如果需要新增一种可能性则可能大动原来的代码,维护起来较为困难。
问题
如何在保持代码可读性的基础上后期维护也比较方便呢?
可以将已知的每一个可能性封装起来,并且使它们可相互替换。让 可能性 独立于条件之外,降低其耦合性。这就是策略模式,又称为 政策模式
简介
环境类(Context): 用一个对象来配置。维护对Strategy对象的引用。
抽象策略类(Strategy): 定义实现的公共接口。 Context使用这个接口来调用 具体实现。
具体策略类(ConcreteStrategy): 以Strategy接口的具体实现
简单演示
/**
* 定义抽象策略(一般是接口后者抽象类):洗衣机洗东西 公用接口
*/
public interface WashingStrategy {
void waterCapacity();
void wash();
}
/**
* 具体策略实现:洗衣机-标准洗
*/
public class StandardWashingStrategy implements WashingStrategy {
@Override
public void waterCapacity() {
System.out.println("标准水容量 。。。。。");
}
@Override
public void wash() {
System.out.println("标准洗 。。。。。");
}
}
/**
* 具体策略实现:洗衣机-标准洗
*/
public class LargeWashingStrategy implements WashingStrategy {
@Override
public void waterCapacity() {
System.out.println("大件水容量 。。。。。");
}
@Override
public void wash() {
System.out.println("大件洗 。。。。。");
}
}
/**
* 具体策略实现:洗衣机-精洗
*/
public class FineWashingStrategy implements WashingStrategy {
@Override
public void waterCapacity() {
System.out.println("精洗水容量 。。。。。");
}
@Override
public void wash() {
System.out.println("精洗 。。。。。");
}
}
/**
* 环境:连接 作用
*/
public class ContextStrategy1 {
private WashingStrategy washingStrategy;
public ContextStrategy1(WashingStrategy washingStrategy) {
this.washingStrategy = washingStrategy;
}
public void run(){
this.washingStrategy.waterCapacity();
this.washingStrategy.wash();
}
}
/**
* 环境:连接 作用
*/
public class ContextStrategy2 {
private WashingStrategy washingStrategy;
public ContextStrategy2(String type) {
switch ( type) {
// 标准洗
case "standard":
washingStrategy = new StandardWashingStrategy();
break;
// 大件洗
case "large":
washingStrategy = new LargeWashingStrategy();
break;
// 精洗
case "fine":
washingStrategy = new FineWashingStrategy();
break;
}
}
public void run(){
this.washingStrategy.waterCapacity();
this.washingStrategy.wash();
}
}
/**
* 方法命名不规范请忽略
*/
public class Test {
public static void main(String[] args) {
//策略模式: 没有if 和 switch;
mode_strategy_1();
//策略模式: 有if 和 switch; 换汤不换药
mode_strategy_2();
}
private static void mode_strategy_2() {
ContextStrategy2 contextStrategy2 = new ContextStrategy2("fine");
contextStrategy2.run();
}
private static void mode_strategy_1() {
ContextStrategy1 contextStrategy1 = new ContextStrategy1(new LargeWashingStrategy());
contextStrategy1.run();
}
}
适用性
1. 许多相关的类仅仅是行为有异。 “策略”提供了多个行为中的某一种行为来配置一个类的方法。即一个系统需要动态的在几种策略中选择一种。
2. 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的策略类中以代替这些条件语句。
优缺点
优点
- 简化了if …else: 提供了用条件语句选择所需的行为以外的另一种选择。当不同的行为堆砌在一个类中时 ,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy类中消除了这些条件语句.
- 提高了代码的可读性,降低了耦合性,增强了扩展性。
缺点
- 必须知道所有的策略类,并决定使用哪一个策略类。
- 各个Strategy实现的策略是简单还是复杂, 它们都共享定义的接口,因此很可能某些 具体实现的参数可能没有用到
- 策略模式将造成产生很多策略类
结合 Spring 在项目中的使用
最近在开发项目的时候,导出数据的时候有多种场景。
/**
* 导出接口(策略模式)
*/
public interface ExportService {
void exportData();
}
/**
* 具体的策略通过 spring 注入
*/
@Component(value = "internal")
public class InternalEvaluationExportServiceImpl implements ExportService {
@Override
public void exportData() {
......
}
}
@Component(value = "mos")
public class MosScoreExportServiceImpl implements ExportService {
@Override
public void exportData() {
......
}
}
@Component(value = "nativeSpeaker")
public class NativeSpeakerEvaluationExportServiceImpl implements ExportService {
@Override
public void exportData() {
......
}
}
/**
* 这里是上下文:策略模式
*/
@Service
public class ExportServiceContext {
// key 就是 ExportService 实现类的 bean 名称
@Autowired
private final Map<String, ExportService> strategyMap = new HashMap<>(6);
public ExportServiceContext(Map<String, ExportService> map){
strategyMap.clear();
map.forEach((k, v) -> this.strategyMap.put(k, v));
}
public void export(String exportServiceId) {
return strategyMap.get(exportServiceId).exportData();
}
}
与 简单工厂模式的区别
/**
* 这里是上下文:工厂模式
*/
@Service
public class ExportServiceContext {
// key 就是 ExportService 实现类的 bean 名称
@Autowired
private final Map<String, ExportService> strategyMap = new HashMap<>(6);
public ExportServiceContext(Map<String, ExportService> map){
strategyMap.clear();
map.forEach((k, v) -> this.strategyMap.put(k, v));
}
public ExportService getExportService(String exportServiceId) {
return strategyMap.get(exportServiceId);
}
}
工厂模式主要是返回的接口实现类的实例化对象
而策略模式是在实例化策略模式的时候已经创建好了,我们可以再策略模式中随意的拼接重写方法,
而工厂模式是不管方法的拼接这些的,他只关注最后的结果,不注重过程,而策略模式注重的是过程。
* 工厂模式中只管生产实例,具体怎么使用工厂实例由调用方决定.
* 策略模式是将生成实例的使用策略放在策略类中配置后才提供调用方使用。
* 工厂模式调用方可以直接调用工厂实例的方法属性等,策略模式不能直接调用实例的方法属性,需要在策略类中封装策略后调用。