欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术的推送!
在我后台回复 「资料」 可领取
编程高频电子书
!
在我后台回复「面试」可领取硬核面试笔记
!文章导读地址:点击查看文章导读!
感谢你的关注!
编程功底-设计模式之策略模式
策略模式在项目中是比较常用的,主要作用是 可以从实现同一个功能的多种方式中选择其中一种进行实现 ,比如说在 RPC 通信中,有多种不同的通信协议如 Dubbo、HTTP、自定义协议等等,那就需要不同的解析算法来针对不同协议进行解析,在具体调用的时候,就需要根据不同的协议来选择不同的协议解析算法,这就可以使用到策略模式
策略模式其他的应用场景还有许多,比如使用优惠券,有不同的优惠策略,那么就需要多种优惠计算实现,使用时再根据具体的优惠策略选择其中一种算法实现
使用策略模式的好处就是:
1、可以根据不同场景切换不同实现策略
2、可以消除代码中的 if-else 代码块
策略模式的结构
如上图,策略模式主要包含 3 种角色:
1、Context: Context 主要维护一个对抽象策略类的引用,这个引用可以指向不同的具体实现策略
2、Strategy: 抽象策略类,所有具体实现策略类的抽象父类
3、ConcreteStrategyA、ConcreteStrategyA: 具体实现策略,集成抽象策略父类,定义具体的实现策略
接下来先根据上边 UML 类图写出一版代码,如下:
// Context 类
public class Context {
private AbstractStrategy strategy;
public void setStrategy(AbstractStrategy strategy) {
this.strategy = strategy;
}
public void resolve() {
strategy.resolve();
}
public static void main(String[] args) {
Context context = new Context();
context.setStrategy(new DubboStrategy());
context.resolve();
}
}
// 抽象策略父类
public abstract class AbstractStrategy {
public abstract void resolve();
}
// 具体策略实现类
public class DubboStrategy extends AbstractStrategy {
@Override
public void resolve() {
System.out.println("Dubbo协议解析");
}
}
// 具体策略实现类
public class HTTPStrategy extends AbstractStrategy {
@Override
public void resolve() {
System.out.println("HTTP协议解析");
}
}
结合 Spring 使用策略模式
上边在使用具体策略的时候,我们还需要手动注入策略,这一点不太方便,而且如果新增策略,我们还需要手动去创建新增的策略,来注入到 Context 中才可以进行使用,因此接下来通过 Spring 进一步对策略模式进行完善,通过 Spring 提供的扩展点 InitializingBean 来扫描创建所有的实现策略,当需要使用时,直接根据 type 来获取对应的实现策略即可
做出优化为:将 Context 类给去掉,换为 ProtocolStrategyFactory 抽象策略工厂,通过策略工厂返回我们需要的策略
UML 类图如下:
代码如下:
// 抽象策略工厂,实现了 InitializingBean,这个是 Spring 提供的扩展点:afterPropertiesSet(),在该方法在扫描所有的具体策略类,注入到 protocolStrategys 属性中
@Component
public class ProtocolStrategyFactory implements InitializingBean {
private final Map<String, AbstractStrategy> protocolStrategys = new HashMap<>();
@Autowired
private ApplicationContext applicationContext;
public AbstractStrategy getStrategy(String type) {
return protocolStrategys.get(type);
}
@Override
public void afterPropertiesSet() throws Exception {
Map<String, AbstractStrategy> strategys = applicationContext.getBeansOfType(AbstractStrategy.class);
strategys.forEach((k, v) -> protocolStrategys.put(v.type(), v));
}
}
// 抽象策略类,这里替换为接口实现,因为 Java 可以单继承、多实现,因此能用接口尽量用接口,将继承的机会省出来
public interface ProtocolStrategy {
public void resolve();
public String type();
}
// 具体策略类
@Component
public class DubboStrategy implements ProtocolStrategy {
@Override
public void resolve() {
System.out.println("Dubbo协议解析");
}
@Override
public String type() {
return "Dubbo";
}
}
// 具体策略类
@Component
public class HTTPStrategy implements ProtocolStrategy {
@Override
public void resolve() {
System.out.println("HTTP协议解析");
}
@Override
public String type() {
return "HTTP";
}
}
// 测试类
@SpringBootTest
class SpringBootProApplicationTests {
@Autowired
private ProtocolStrategyFactory protocolStrategyFactory;
@Test
void contextLoads() {
AbstractStrategy dubbo = protocolStrategyFactory.getStrategy("Dubbo");
dubbo.resolve();
}
}
这样在新增策略的时候,只需要新增具体的实现策略,工厂会自动取扫描所有的实现策略,并且进行保存,如果不基于 Spring 的扩展点来做的话,当新增策略时需要手动将新增策略放入工厂中,违反了 开闭原则
因此策略模式和 Spring 结合使用,让开发者可以很方便的获取实现策略,并且新增策略也不会违反开闭原则
开发者获取实现策略时,直接根据 type 去工厂中获取对应的实现策略即可,这里的 type 可以使用常量给抽取出来,这里为了看起来方便,没有进一步抽取
策略模式优点
项目中引入策略模式,优点如下:
1、抽象策略与具体实现策略分离,利用继承和多态来管理多个策略类,优化代码结构
2、避免大量使用 if else,如果没有策略模式对具体策略统一管理,那么每新增一个策略模式,都要根据条件进行 if 判断,决定使用哪一个具体策略;有了策略模式之后,就可以将 type 标识传入策略工厂,让工厂返回一个具体实现策略
3、提升代码复用性,将具体实现策略抽取出来,可以在不同环境中进行复用
4、可以很方便的切换实现策略,只需要指定要用到的策略即可,其余工作都由策略工厂来完成
最后总结一下,通过策略模式,在不修改原有代码的基础上可以选择使用不同的策略,并且可以灵活的增加新的