看了架构师的代码,切身感受到了代码可扩展性高的魅力

看了架构师的代码,切身感受到了代码可扩展性高的魅力

技术杂谈小彩虹2021-08-19 1:02:04130A+A-

前言

开发有个著名的设计原则:开闭原则,即对扩展开放,对修改关闭。但是实际开发中鲜有人能运用纯熟,少侠在开发中接触的例子就是,大多数人就是if…else…这样难以扩展的条件判断。那么应该如何优雅的精简掉复杂的逻辑判断呢?当然抽象共性是从产品思维角度的优化方案,今天少侠想说的是通过技术手段实现。

业务背景

首先简单介绍一下业务背景,背景很简单,就是有若干渠道源,如阿里巴巴,腾讯等,针对不同的渠道需要有不同的数据处理逻辑,并且渠道来源之后会不断扩展。

初步实现

先建立一个简单的枚举类:

/** * @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注解调用):

/** * @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注解)。

@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;
    }
}

然后接口层的改造如下,注意看这里的注解只是配置了接口的自动装配(并未注解所有的实现类),然后通过多态实现具体调用

@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注解。

点点关注,不会迷路

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们