看了架構師的代碼,切身感覺到了代碼可擴展性高的魅力

前言

開發有個著名的設計原則:開閉原則,即對擴展開放,對修改關閉。可是實際開發中鮮有人能運用純熟,少俠在開發中接觸的例子就是,大多數人就是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註解。對象

點點關注,不會迷路

相關文章
相關標籤/搜索