海珠营销型网站建设公司,小程序开发平台哪家服务好,企业建设网站要注意的要素,wordpress扫描附件到新浪图床1 什么是策略模式 策略模式#xff08;Strategy Pattern#xff09;是一种常用的面向对象设计模式#xff0c;它定义了一系列可互相替换的算法或策略#xff0c;并将每个算法封装成独立的对象#xff0c;使得它们可以在运行时动态地替换。具体来说#xff0c;策略模式定义…1 什么是策略模式 策略模式Strategy Pattern是一种常用的面向对象设计模式它定义了一系列可互相替换的算法或策略并将每个算法封装成独立的对象使得它们可以在运行时动态地替换。具体来说策略模式定义了一系列算法每个算法都封装在一个具体的策略类中这些策略类实现了相同的接口或抽象类。在使用算法的时候客户端通过一个上下文对象来调用策略类的方法从而完成算法的执行。这样客户端可以在运行时动态地选择不同的策略类从而实现不同的行为。用一句话来说就是“准备一组算法并将每一个算法封装起来使得它们可以互换”。 这个模式涉及到三个角色 ● 环境(Context)角色持有一个Strategy的引用。 ● 抽象策略(Strategy)角色这是一个抽象角色通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。 ● 具体策略(ConcreteStrategy)角色包装了相关的算法或行为。 2 认识策略模式 策略模式的重心: 策略模式的重心不是如何实现算法而是如何组织、调用这些算法从而让程序结构更灵活具有更好的维护性和扩展性。 算法的平等性: 策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法大家的地位是完全一样的正因为这个平等性才能实现算法之间可以相互替换。所有的策略算法在实现上也是相互独立的相互之间是没有依赖的。 所以可以这样描述这一系列策略算法策略算法是相同行为的不同实现。 运行时策略的唯一性: 运行期间策略模式在每一个时刻只能使用一个具体的策略实现对象虽然可以动态地在不同的策略实现中切换但是同时只能使用一个。 公有的行为: 经常见到的是所有的具体策略类都有一些公有的行为。这时候就应当把这些公有的行为放到共同的抽象策略角色Strategy类里面。当然这时候抽象策略角色必须要用Java抽象类实现而不能使用接口。
3 策略模式的优点和缺点
策略模式的优点 1策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面从而避免代码重复。 2使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起统统列在一个多重条件语句里面比使用继承的办法还要原始和落后。 策略模式的缺点 1客户端必须知道所有的策略类并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别以便适时选择恰当的算法类。换言之策略模式只适用于客户端知道算法或行为的情况。 2由于策略模式把每个具体的策略实现都单独封装成为类如果备选的策略很多的话那么对象的数目就会很可观。 4 策略模式适用场景 (1) 当一个系统中存在多个类只有它们的行为或算法不同时。 (2) 当一个类定义了多种行为而这些行为在这个类的操作中以多个条件语句的形式出现可以将相关的条件分支移入它们各自的策略类中以替换这些条件语句。 (3) 当系统需要动态地在几种算法中选择一种时如根据不同的配置、用户选择或者环境条件等。 5 策略模式的最佳实践重点 策略模式是一个非常简单且常用的设计模式策略模式最常见的作用就是解决代码中冗长的 if-else 或 switch 分支判断语句同时也可以提高程序的扩展性和灵活性避免代码重复
策略模式的使用包含三部分策略的定义、创建和使用。 5.1 策略的定义、创建和使用
5.1.1 策略的定义 策略类的定义比较简单包含一个策略接口和一组实现这个接口的策略类。所有的策略类都实现相同的接口所以客户端代码基于接口而非实现编程可以灵活地替换不同的策略。 public interface Strategy {void algorithmInterface();
}public class ConcreteStrategyA implements Strategy {Overridepublic void algorithmInterface() {//具体的算法...}
}public class ConcreteStrategyB implements Strategy {Overridepublic void algorithmInterface() {//具体的算法...}
} 如上述代码所示定义了一个策略接口 Strategy具体的策略实现类都实现 Strategy 接口并重写 algorithmInterface 方法最后客户端根据不同的策略即可调用不同实现类的 algorithmInterface 方法。
5.1.2 策略的创建 可以将创建策略的代码逻辑抽象到工厂类中提前在工厂类创建好所有策略类缓存在 Map 中。Map 的 key 为策略类型value 为具体的策略实现类。当需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类。
public class StrategyFactory {// Map 的 key 为策略类型value 为具体的策略实现类private static final MapString, Strategy strategies new HashMap();// 提前创建好所有策略类缓存到 Map 中static {strategies.put(A, new ConcreteStrategyA());strategies.put(B, new ConcreteStrategyB());}// 需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类public static Strategy getStrategy(String type) {if (type null || type.isEmpty()) {throw new IllegalArgumentException(type should not be empty.);}return strategies.get(type);}
} 这里重点在于使用”查表法”代替了大量的分支判断即每次根据 type 去 Map 中获取省略了大量的 if-else。
5.1.3 策略的使用 具体使用 A、B、C 何种策略在具体的场景可以会根据系统的配置来选择。可以从配置文件中读取出配置然后传递给策略工厂类 StrategyFactory 的 getStrategy 方法即可获取到相应的策略类。最后调用策略类的 algorithmInterface 方法去执行代码逻辑。
代码如下所示省略了从配置文件中读取配置的流程。 // 根据 type 的不同执行不同分支的代码逻辑
private void process(String type){Strategy strategy StrategyFactory.getStrategy(type);
// 调用策略类的 algorithmInterface 方法去执行代码逻辑
strategy.algorithmInterface();
}
5.1.4 当前设计是否容易扩展性 最原始的 process 方法中首先会判断各种 type然后执行不同类型的代码逻辑。如果需要扩展新的 D、E、F 类型需要大量修改 process 方法。
优化后如果需要扩展新的 D、E、F 类型流程如下
定义相应的 D、E、F 类型的策略实现类提前在策略工厂类 StrategyFactory 中创建相应的策略实现类并添加到 Map 中客户端代码不用进行任何改动即process 方法不需要进行改动 优化后的代码相对来说职责更加单一且对调用方非常友好。调用方代码不需要任何改动即可使用新的策略。要做的可能就是在配置文件中配置新的策略即可。
5.2 有状态的策略类不能提前创建 在策略类的创建部分在类初始化时将所有的策略类提前创建好存放在 Map 中。当需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类。 假设策略类是有状态的每次获取策略对象时都要求创建新的策略类。此时就不能使用 Map 缓存的方式来优化代码结构了。可以使用如下方式实现策略工厂类 public class StrategyFactory {public static Strategy getStrategy(String type) {if (type null || type.isEmpty()) {throw new IllegalArgumentException(type should not be empty.);}if (type.equals(A)) {return new ConcreteStrategyA();} else if (type.equals(B)) {return new ConcreteStrategyB();}return null;}
}
上述代码又退化回了 if-else 嵌套当然也可以优化为 switch-case 的设计。 极客时间-王争老师的《设计模式之美》课程第 60 节最后的课堂讨论中留下了一道题目在策略工厂类中如果每次都要返回新的策略对象我们还是需要在工厂类中编写 if-else 分支判断逻辑那这个问题该如何解决呢 笔者看到文末大家的回答点赞数最高的评论是仍然可以用查表法只不过存储的不再是实例而是class使用时获取对应的class再通过反射创建实例。 反射在这里应该是可以实现但是笔者感觉不是非常灵活假设策略实现类需要在这里调用一些有参构造器且不同的策略类的有参构造器需要传入的参数不同那么反射实现起来不是非常灵活。 例如 ConcreteStrategyA 的构造器需要传入 ageConcreteStrategyB 的构造器需要传入 date。对于这样的 case反射不太好实现如果实现出来也是一对 if-else 分支判断。 文末也没有其他令笔者眼前一亮的回答反倒是笔者在阅读 Flink 源码的过程中发现了一个笔者感觉比较优秀的解决方案下面就到了秀操作环节。
5.2.1 秀操作
首先分析一个问题来源
对于无状态的策略类将所有的策略类提前创建好存放在 Map 中。当需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类。对于有状态的策略类不能提前创建所有的策略类所以没办法提前创建好将其存放在 Map 中
换种思路
给每个具体的策略类创建相应的策略类工厂。例如 ConcreteStrategyA 的工厂为 StrategyFactoryA ConcreteStrategyB 的工厂为 StrategyFactoryB。虽然没办法提前创建好策略类放到 Map 中但是可以将策略类的工厂类提前创建好放到 Map 中。根据传入的 type 就可以从 Map 中获取相应策略类的工厂类然后执行工厂类的 create 方法即可创建出相应的策略类。
根据上述思路实现相应代码。
首先定义策略工厂接口并分别实现策略 A 和策略 B 的工厂类 // 策略工厂接口
public interface StrategyFactory {Strategy create();
}// 策略 A 的工厂类用于创建策略 A
public class StrategyFactoryA implements StrategyFactory{Overridepublic Strategy create() {return new ConcreteStrategyA();}
}// 策略 B 的工厂类用于创建策略 B
public class StrategyFactoryB implements StrategyFactory{Overridepublic Strategy create() {return new ConcreteStrategyB();}
}
对外开放的工厂实现如下 public class Factory {// Map 的 key 为策略类型value 为 策略的工厂类private static final MapString, StrategyFactory STRATEGY_FACTORIES new HashMap();static {// 将各种实现类的工厂提前创建好放到 Map 中STRATEGY_FACTORIES.put(A, new StrategyFactoryA());STRATEGY_FACTORIES.put(B, new StrategyFactoryB());}public static Strategy getStrategy(String type) {if (type null || type.isEmpty()) {throw new IllegalArgumentException(type should not be empty.);}// 根据 type 获取对应的策略工厂StrategyFactory strategyFactory STRATEGY_FACTORIES.get(type);// 调用具体工厂类的 create 方法即可创建出相应的策略类return strategyFactory.create();}
} Factory 类中定义了 MapMap 的 key 为策略类型value 为 策略的工厂类。Factory 类初始化时将各种实现类的工厂提前创建好放到 Map 中。 Factory 类的静态方法 getStrategy 用于根据 type 创建相应的策略类getStrategy 方法根据 type 从 Map 中获取 type 对应的策略类的工厂调用具体工厂类的 create 方法即可创建出相应的策略类。
当策略类的构造方法比较复杂也没关系封装在策略类相应的工厂中即可。 旧方案对于每次要创建新策略类的场景要搞一堆 if-else 分支判断上述流程使用 Map 优化了 if-else 分支判断逻辑。但带来了一个新的问题即创建出了很多类相比之前的实现来讲多了 StrategyFactoryA 和 StrategyFactoryB 类。 为了代码的简洁可以利用 Java8 的 lambda 表达式将 StrategyFactoryA 和 StrategyFactoryB 类优化掉。截取上述部分代码实现 // 策略工厂接口
public interface StrategyFactory {Strategy create();
}// 策略 A 的工厂类用于创建策略 A
public class StrategyFactoryA implements StrategyFactory{Overridepublic Strategy create() {return new ConcreteStrategyA();}
}MapString, StrategyFactory STRATEGY_FACTORIES new HashMap();
// 将各种实现类的工厂提前创建好放到 Map 中
STRATEGY_FACTORIES.put(A, new StrategyFactoryA());
上述代码使用 lambda 优化后 // 策略工厂接口
public interface StrategyFactory {Strategy create();
}MapString, StrategyFactory STRATEGY_FACTORIES new HashMap();
// 将各种实现类的工厂提前创建好放到 Map 中
STRATEGY_FACTORIES.put(A, () - new ConcreteStrategyA());
关于 lambda 这里就不多解释了。lambda 表达式还能优化为 Java8 的方法引用代码如下所示
STRATEGY_FACTORIES.put(“A”, ConcreteStrategyA::new);
5.2.1 小结
把上述整个代码的最终版贴在这里 // 策略工厂接口
public interface StrategyFactory {Strategy create();
}public class Factory {// Map 的 key 为策略类型value 为 策略的工厂类private static final MapString, StrategyFactory STRATEGY_FACTORIES new HashMap();static {// 将各种实现类的工厂提前创建好放到 Map 中STRATEGY_FACTORIES.put(A, ConcreteStrategyA::new);STRATEGY_FACTORIES.put(B, ConcreteStrategyB::new);}public static Strategy getStrategy(String type) {if (type null || type.isEmpty()) {throw new IllegalArgumentException(type should not be empty.);}// 根据 type 获取对应的策略工厂StrategyFactory strategyFactory STRATEGY_FACTORIES.get(type);// 调用具体工厂类的 create 方法即可创建出相应的策略类return strategyFactory.create();}
} 代码量相比之前的策略类可以共享的代码设计来讲只是增加了一个 StrategyFactory 接口的设计所以整体代码也是非常简洁的。 5.3 策略模式在项目中的真实实现
5.3.1 实际需求 智慧停车的功能模块由于各个地方项目上用到的第三方车厂厂商的设备不一致如 科托捷顺红门零壹等对应需要要对接的接口也比较多虽然对接的车厂不一样但是在我们业务系统中要做的事都是一样的如都需要通过我们app来开通月卡续费扫码出场车辆预约等只是这些功能的实现需要去调用不同车厂的提供的相似功能的接口去实现 该系统需要动态地在几种算法中选择一种时如根据不同的配置来选择因此选择策略模式是比较合适的这样可以程序结构更灵活具有更好的维护性和扩展性(后续如果要加入其他的第三方车厂就很方便拓展了)策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面从而避免代码重复. 5.3.2 实践
策略的定义 策略类的定义比较简单包含一个策略接口和一组实现这个接口的策略类。所有的策略类都实现相同的接口所以客户端代码基于接口而非实现编程可以灵活地替换不同的策略。
public interface ThirdParkVersionService {/**--------------------------------科托车场厂商相关接口对接--------------------------------------------*/KeyTopRespCarsCardVOs getCarCardList(CarCardDTOs dto);/**--------------------------------捷顺车场厂商相关接口对接--------------------------------------------*/JsstRespDelaybymodeVO delaybymode(DelaybymodeDTO delaybymodeDTO);
}
/*** Description 聚合不同厂商的默认实现 * version1.0 利用接口默认方法进行空实现防止下层接口需要实现过多方法*/
public interface ThirdParkDefaultService extends ThirdParkVersionService{static String getThirdConfigKey(String thirdParkId,Long projectId){return projectId.toString().concat(thirdParkId);}/**--------------------------------------科托厂商接口默认实现-------------------------------------------------*//*** Description:* Param: [carCardDTOs]* return: com.bzcst.bop.iot.car.manager.entity.common.response.KeyTopRespcom.bzcst.bop.iot.car.manager.entity.keytopvo.CarsCardVOs* Author: xiongguoshuang* Exception: 获取固定车列表* Date: 2022/5/9 13:51*/Overridedefault KeyTopRespCarsCardVOs getCarCardList(CarCardDTOs dto) {return null;}
......................}public interface KeyTopService extends ThirdParkDefaultService{//实现科托相关接口}
public interface JsstService extends ThirdParkDefaultService {// 实现捷顺相关接口
}Component(ThirdParkConstants.BUSINESS_TYPE_KEYTOP_VER_FIVE)
public class KeyTopFiveServiceImpl implements KeyTopService{//接口实现类
}
Component(ThirdParkConstants.BUSINESS_TYPE_JSST_VER_THIRD)
public class JsstThirdServiceImpl implements JsstService {//接口实现类
}
策略的创建 可以将创建策略的代码逻辑抽象到工厂类中提前在工厂类创建好所有策略类缓存在 Map 中。Map 的 key 为策略类型value 为具体的策略实现类。当需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类。这里的Map 通过服务类自定义的类名自动注入实现 Component
public class ThirdParkFactorySelector {Resourceprivate MapString, ThirdParkFactoryService handlers;public ThirdParkFactoryService thirdParkFactoySelector(String providerCode) {ThirdParkFactoryService thirdParkFactoryService handlers.get(providerCode);return thirdParkFactoryService;}
}Component
public class ThirdParkFactoryServiceSelector {ResourceDeviceProviderApiService deviceProviderApiService;ResourceCarYardConfigService carYardConfigService;ResourceThirdParkFactorySelector thirdParkFactorySelector;/*** 根据车场ID获取对应的车场厂商实例 调用需要实现的业务逻辑*/public ThirdParkFactoryService getThirdParkFactoryService(Long carParkId) {CarYardConfig carYardConfig getCarYardConfigByParkId(carParkId);Long partnerId carYardConfig.getPartnerId();if(partnerId null){throw new BusinessException(根据carParkId未找到对应的partnerId);}DeviceProviderVO provider deviceProviderApiService.get(partnerId);return thirdParkFactorySelector.thirdParkFactoySelector(provider.getCode());}} 策略的使用 具体在业务代码中调用那个车厂厂商的接口是根据系统配置表中具体的配置来选择从数据表中读取具体的配置调用getThirdParkFactoryService获取对应的策略类最后调用策略类的中具体的方法去执行代码逻辑。