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

前言

开发有个著名的设计原则:开闭原则,即对扩展开放,对修改关闭。可是实际开发中鲜有人能运用纯熟,少侠在开发中接触的例子就是,大多数人就是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注解。对象

点点关注,不会迷路

相关文章
相关标签/搜索