关于策略模式、模板模式和工厂模式的基础概念和优缺点可以自行了解一下,这里主要讲的是如何优雅地使用这三种模式保证服务符合:SRP(单一职责原则)和OCP(开闭原则)、耦合度低、可扩展性高和减少大量if else代码的场景。
策略模式:
1.环境(Context)角色:持有一个Strategy的引用。
2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
3.具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
这种的经典并简单的策略模式大家也许已经使用过了,但是这样的策略模式会有一个缺点:
策略模式适用于多类型场景,调用策略时必定会有大量的if else,后续如有新的类型的策略需要被使用时则需要增加if else,代码改动较大,从而导致该模块可扩展性不高且会产生大量if else代码,不易维护。
为更好体验到优化的过程,首先给一个需求背景:
某服务需要展示给用户三种不同的趋势图:指数变化趋势图、新增课件数趋势图、新增点评数趋势图。
模块优化Round 1:
为了解决以上的问题,使用策略模式+自定义注解+模板模式(模板模式和优化无关,只是业务需要)来设计一下:
首先需要抽象父类策略的定义:
1 | 复制代码@Slf4j |
可以看到,handle(EducationQuery query)方法是统一处理方法,子类可以继承也可以默认使用父类方法。getTrendChartData(EducationQuery query)是模板方法,子类一定会执行该方法,并可以重写父类的getValuesOperation(List
三个子类如下:
1 | 复制代码@Service |
1 | 复制代码@Service |
1 | 复制代码@Service |
可以看到,子类NewClassesHandler和NewCommentsHandler类都默认使用了父类的模板,实现了这两种趋势图的逻辑。而PigeonsIndexHandler类则重写了父类的模板中的方法,使用了父类的逻辑后使用子类中重写的逻辑。
以上是策略规则,接下来是策略获取类 TrendChartHandlerContext类:
1 | 复制代码@SuppressWarnings("unchecked") |
该类用于将前端传入的type转为子类对象。
TrendChartHandlerContext中的handlerMap的值则为这三种趋势图的类型的枚举中的值。由
TrendChartHandlerProcessor类统一扫描自定义注解的值,并统一将类型和子类对象放入handlerMap中。
使用策略:
1 | 复制代码 /** |
service逻辑实现:
1 | 复制代码 public List<TrendChartDataVo> queryTrendChartData(EducationQuery query) { |
可以看到,使用策略时只需要调用策略的handler方法即可,无需关注type,规避掉大量的if else代码。
工具类:
1 | 复制代码@Component |
1 | 复制代码public class ClassScaner implements ResourceLoaderAware { |
这样就算解决类if else的问题了,但是大家会发现有大量的工具需要引入,增加了许多代码量,看上去会不够美观。
模块优化Round 2:
未完待续
本文转载自: 掘金