开发有个著名的设计原则:开闭原则,即对扩展开放,对修改关闭。可是实际开发中鲜有人能运用纯熟,少侠在开发中接触的例子就是,大多数人就是if...else...这样难以扩展的条件判断。那么应该如何优雅的精简掉复杂的逻辑判断呢?固然抽象共性是从产品思惟角度的优化方案,今天少侠想说的是经过技术手段实现。java
首先简单介绍一下业务背景,背景很简单,就是有若干渠道源,如阿里巴巴,腾讯等,针对不一样的渠道须要有不一样的数据处理逻辑,而且渠道来源以后会不断扩展。cookie
先创建一个简单的枚举类:app
/** * @author Carson * @date 2020/8/24 下午3:10 */ public enum SourceEnum { /** * 阿里巴巴 */ ALIBABA("ALIBABA"), /** * 腾讯 */ TENCENT("TENCENT"), ; public String name; SourceEnum(String name) { this.name = name; } //匹配 public static SourceEnum match(String name){ SourceEnum[] values = SourceEnum.values(); for (SourceEnum value : values) { if(value.name.equals(name)){ return value; } } return null; } public String getName() { return name; } }
再来看看业务层接口和具体的实现类(注意实现类上的@Service注解是指定的了别名的,方便以后利用 @Qualifier注解调用):ide
/** * @author Carson * @date 2020/8/24 下午3:10 */ public interface DataService { String dataProcess(); } /** * @author Carson * @date 2020/8/24 下午3:14 */ @Service("alibabaServiceImpl") public class AlibabaServiceImpl implements DataService { @Override public String dataProcess() { return "Alibaba Process++++++++++++++"; } } /** * @author Carson * @date 2020/8/24 下午3:12 */ @Service("tencentServiceImpl") public class TencentServiceImpl implements DataService { @Override public String dataProcess() { return "Tencent Process++++++++++++++"; } }
再来看看接口层的实现,主要是让用户传入渠道源source(真实环境中这个多是从cookies里的用户信息获取,这里且不讨论),而后根据不一样的渠道进行判断,注意看代码里的switch...case...语句,若是以后渠道扩展了,这里势必是要作改动的。有人可能以为,看着还阔以啊,干净整洁,可是以后要是渠道不少呢,万一百八十个渠道,这里看着就很臃肿,而且万一出错排查起来也不方便,说得专业一点,就是扩展性很差。优化
/** * @author Carson * @date 20-8-20 下午5:00 */ @RestController public class CommonController { private final Logger logger = LoggerFactory.getLogger(CommonController.class); @Qualifier(value = "alibabaServiceImpl") @Autowired private AlibabaServiceImpl alibabaServiceImpl; @Qualifier(value = "tencentServiceImpl") @Autowired private TencentServiceImpl tencentServiceImpl; @GetMapping("/dataHandler") public String dataHandler(String source) { if (StringUtils.isBlank(source)) { return "Empty data"; } SourceEnum sourceEnum = SourceEnum.match(source); if (sourceEnum == null) { return "Empty data"; } switch (sourceEnum) { case ALIBABA: return alibabaServiceImpl.dataProcess(); case TENCENT: return tencentServiceImpl.dataProcess(); default: return "Empty data"; } } }
优化的第一步是从枚举类开始,为每一个枚举类指定对应的Service实现类对象,注意,Spring中接口不能被加载成Bean实例,因此须要为属性和方法添加@Lookup
注解)。this
@Service public interface DataService { // 注意,此处必须加此@Lookup注解,不然容器没法启动 @Lookup String dataProcess(); }
public class AlibabaServiceImpl implements DataService { public AlibabaServiceImpl(){} @Override public String dataProcess() { return "Alibaba Process++++++++++++++"; } } public class TencentServiceImpl implements DataService { @Override public String dataProcess() { return "Tencent Process++++++++++++++"; } }
而后是改进后的枚举类:设计
/** * @author Carson * @date 2020/8/24 下午3:10 */ public enum SourceEnum { /** * 阿里巴巴 */ ALIBABA("ALIBABA", new AlibabaServiceImpl()), /** * 腾讯 */ TENCENT("TENCENT", new TencentServiceImpl()), ; public String name; public DataService dataService; SourceEnum(String name, DataService dataService) { this.name = name; this.dataService = dataService; } //匹配 public static SourceEnum match(String name) { SourceEnum[] values = SourceEnum.values(); for (SourceEnum value : values) { if (value.name.equals(name)) { return value; } } return null; } public String getName() { return name; } public DataService getDataService() { return dataService; } }
而后接口层的改造以下,注意看这里的注解只是配置了接口的自动装配(并未注解全部的实现类),而后经过多态实现具体调用。code
@RestController public class CommonController { private final Logger logger = LoggerFactory.getLogger(CommonController.class); @Autowired private DataService dataService; @GetMapping("/dataHandler") public String dataHandler(String source) { if (StringUtils.isBlank(source)) { return "Empty data"; } SourceEnum sourceEnum = SourceEnum.match(source); if (sourceEnum == null) { return "Empty data"; } AbstractDataService dataService = sourceEnum.dataService; if (dataService == null) { return "Empty data"; } return dataService.dataProcess(); } }
经过使用枚举类,在枚举中将 属性与规则具体实现进行绑定。经过改变能够减小if -else使得代码更加优雅 若是须要新增渠道,咱们只须要在编写具体规则实现类时实现DataService接口,并在枚举类中新增的枚举,而不须要改动到原先的任何代码。这符合了开闭原则。
因为Spring默认是单例模式,因此正常开发一个接口有多个实现类都是一个一个指定(@Qualifier
)装配,可是若是想利用多态,让程序决定由哪一个具体实现类执行,能够将@Service
注解配置在接口上,同时为接口属性/方法添加@Lookup
注解。对象