使用設(shè)計模式斋泄,干掉 if else 語句

場景

1. 場景介紹

公司在做一個物聯(lián)卡相關(guān)的項目時,需要對多個卡供應(yīng)商(移動镐牺、聯(lián)通炫掐、電信的下級卡片代理商,公司已經(jīng)接入了5家卡片供應(yīng)商)的接口進行調(diào)用睬涧,我負責將不同供應(yīng)商的接口進行整合募胃,形成一個統(tǒng)一的調(diào)用接口,大概的流程如下畦浓。


image

比較常見的實現(xiàn)方式是通過if - else if - else來進行不同供應(yīng)商的選擇摔认,但是使用if-else 時,隨著供應(yīng)商的不斷增多宅粥,else if的代碼會越來越多,越來越復(fù)雜参袱,不利于擴展,違反了開閉原則(對擴展開放秽梅,對修改關(guān)閉)

2. 其他類似場景

2.1 接入多家三方支付公司接口

2.2 接入多種加密方式

2.3 接入多種登錄方式

2.4 接入多家物流公司接口

3. 如何干掉 if-else 語句抹蚀?

使用工廠方法模式+模板方法模式,感覺我的代碼實現(xiàn)和這兩個設(shè)計模式比較接近企垦,代碼結(jié)構(gòu)如下环壤。


image

代碼

1. 配置

1.1 添加配置類

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Map;

@Data
@Component
@ConfigurationProperties(prefix = "esim")
public class EsimSupplierProp {

    private Map<Long, SupplierInfo> suppliers;

    @Data
    public static class SupplierInfo {
        private String name;
        private String className;
    }

}

1.2 添加配置文件

配置供應(yīng)商key(supplierId)、name(供應(yīng)商名稱)钞诡、class-name(全類名郑现,通過反射創(chuàng)建實例)

esim:
  suppliers:
    1:
      name: 中國移動
      class-name: com.gnl.auth.test.service.SupplierYiDong
    2:
      name: 中國聯(lián)通
      class-name: com.gnl.auth.test.service.SupplierLianTong
    3:
      name: 中國電信
      class-name: com.gnl.auth.test.service.SupplierDianXin

2. 添加實體類

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Esim {
    private Long esimId;
    private Long esimSupplierId;
    private String msisdn;
    private BigDecimal packagePrice;
    private BigDecimal packageCapacity;
}

3. 添加供應(yīng)商模板方法抽象類

import com.gnl.auth.test.config.EsimSupplierProp;
import com.gnl.auth.test.entity.Esim;
import lombok.Data;

@Data
public abstract class EsimSupplier {

    /**
     * 供應(yīng)商信息
     */
    private EsimSupplierProp.SupplierInfo supplierInfo;

    /**
     * 卡信息
     */
    public abstract Esim info(Esim esim);

    /**
     * 停機
     */
    public abstract Boolean stop(Esim esim);

    /**
     * 復(fù)機
     */
    public abstract Boolean reply(Esim esim);

    /**
     * 卡充值
     */
    public Boolean recharge(Esim esim) {
        throw new RuntimeException(supplierInfo.getName() + "不支持充值操作");
    }

}

4. 添加供應(yīng)商工廠類并注入

添加工廠類

方法一:通過配置文件和反射拿到所有供應(yīng)商實現(xiàn)類

import com.gnl.auth.test.config.EsimSupplierProp;
import com.gnl.auth.test.service.EsimSupplier;

import java.util.HashMap;
import java.util.Map;

public class EsimSupplierFactory {
    /**
     * 供應(yīng)商服務(wù)map
     */
    private Map<Long, EsimSupplier> esimSupplierMap;

    /**
     * 構(gòu)造函數(shù)
     *
     * @param esimSupplierProp 配置文件
     */
    public EsimSupplierFactory(EsimSupplierProp esimSupplierProp) {
        // 初始化map
        this.esimSupplierMap = new HashMap<>();
        // 遍歷配置文件
        Map<Long, EsimSupplierProp.SupplierInfo> suppliers = esimSupplierProp.getSuppliers();
        for (Map.Entry<Long, EsimSupplierProp.SupplierInfo> supplier : suppliers.entrySet()) {
            try {
                // 使用反射創(chuàng)建供應(yīng)商服務(wù)對象,并設(shè)置進map中
                Class<?> classBook = Class.forName(supplier.getValue().getClassName());
                EsimSupplier esimSupplier = (EsimSupplier) classBook.newInstance();
                // 設(shè)置供應(yīng)商信息
                esimSupplier.setSupplierInfo(supplier.getValue());
                esimSupplierMap.put(supplier.getKey(), esimSupplier);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(supplier.getValue().getName() + "供應(yīng)商不存在", e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(supplier.getValue().getName() + "供應(yīng)商不存在", e);
            } catch (InstantiationException e) {
                throw new RuntimeException(supplier.getValue().getName() + "供應(yīng)商不存在", e);
            }
        }
    }

    /**
     * 獲取供應(yīng)商服務(wù)
     */
    public EsimSupplier getEsimSupplier(Long supplierId) {
        EsimSupplier esimSupplier = esimSupplierMap.get(supplierId);
        if (esimSupplier == null) {
            throw new RuntimeException("卡供應(yīng)商不存在");
        }
        return esimSupplier;
    }

}

注入工廠類

import com.gnl.auth.test.factory.EsimSupplierFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class EsimSupplierConfig {
    /**
     * 注入工廠
     */
    @Bean
    public EsimSupplierFactory EsimSupplierFactory(EsimSupplierProp esimSupplierProp) {
        return new EsimSupplierFactory(esimSupplierProp);
    }
}

5. 添加不同供應(yīng)商實現(xiàn)類

5.1 中國移動

import com.alibaba.fastjson.JSON;
import com.gnl.auth.test.config.EsimSupplierProp;
import com.gnl.auth.test.entity.Esim;
import lombok.extern.log4j.Log4j2;

import java.math.BigDecimal;

@Log4j2
public class SupplierYiDong extends EsimSupplier {
    @Override
    public Esim info(Esim esim) {
        Esim info = Esim.builder()
                .esimId(esim.getEsimId())
                .esimSupplierId(1L)
                .msisdn("1800000000" + esim.getEsimId())
                .packageCapacity(new BigDecimal(2048))
                .packagePrice(new BigDecimal(22))
                .build();
        log.info("移動卡查詢成功: {}", JSON.toJSONString(info));
        EsimSupplierProp.SupplierInfo supplierInfo = this.getSupplierInfo();
        log.info("移動配置: {}",JSON.toJSONString(supplierInfo));
        return info;
    }

    @Override
    public Boolean stop(Esim esim) {
        log.info("移動卡停機成功: {}", esim.getEsimId());
        return true;
    }

    @Override
    public Boolean reply(Esim esim) {
        log.info("移動卡復(fù)機成功: {}", esim.getEsimId());
        return true;
    }
}

5.1 中國聯(lián)通

import com.alibaba.fastjson.JSON;
import com.gnl.auth.test.config.EsimSupplierProp;
import com.gnl.auth.test.entity.Esim;
import lombok.extern.log4j.Log4j2;

import java.math.BigDecimal;

@Log4j2
public class SupplierLianTong extends EsimSupplier {
    @Override
    public Esim info(Esim esim) {
        Esim info = Esim.builder()
                .esimId(esim.getEsimId())
                .esimSupplierId(2L)
                .msisdn("1800000000" + esim.getEsimId())
                .packageCapacity(new BigDecimal(3072))
                .packagePrice(new BigDecimal(33))
                .build();
        log.info("聯(lián)通卡查詢成功: {}", JSON.toJSONString(info));
        EsimSupplierProp.SupplierInfo supplierInfo = this.getSupplierInfo();
        log.info("聯(lián)通配置: {}", JSON.toJSONString(supplierInfo));
        return info;
    }

    @Override
    public Boolean stop(Esim esim) {
        log.info("聯(lián)通卡停機成功: {}", esim.getEsimId());
        return true;
    }

    @Override
    public Boolean reply(Esim esim) {
        log.info("聯(lián)通卡復(fù)機成功: {}", esim.getEsimId());
        return true;
    }
}

5.1 中國電信

import com.alibaba.fastjson.JSON;
import com.gnl.auth.test.config.EsimSupplierProp;
import com.gnl.auth.test.entity.Esim;
import lombok.extern.log4j.Log4j2;

import java.math.BigDecimal;

@Log4j2
public class SupplierDianXin extends EsimSupplier {

    @Override
    public Esim info(Esim esim) {
        Esim info = Esim.builder()
                .esimId(esim.getEsimId())
                .esimSupplierId(3L)
                .msisdn("1800000000" + esim.getEsimId())
                .packageCapacity(new BigDecimal(6144))
                .packagePrice(new BigDecimal(66))
                .build();
        log.info("電信卡查詢成功: " + JSON.toJSONString(info));
        EsimSupplierProp.SupplierInfo supplierInfo = this.getSupplierInfo();
        log.info("電信配置: {}",JSON.toJSONString(supplierInfo));
        return info;
    }

    @Override
    public Boolean stop(Esim esim) {
        log.info("電信卡停機成功: {}", esim.getEsimId());
        return true;
    }

    @Override
    public Boolean reply(Esim esim) {
        log.info("電信卡復(fù)機成功: {}", esim.getEsimId());
        return true;
    }

    public Boolean recharge(Esim esim){
        log.info("電信卡充值成功: {}", esim.getEsimId());
        return true;
    }
}

6. 測試

6.1 添加測試類

import com.gnl.auth.test.entity.Esim;
import com.gnl.auth.test.factory.EsimSupplierFactory;
import com.gnl.auth.test.service.EsimSupplier;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class EsimTest {

    @Autowired
    private EsimSupplierFactory esimSupplierFactory;

    @Test
    void test1() {
        // 移動
        Esim esim = Esim.builder().esimId(1L).esimSupplierId(1l).build();
        // 聯(lián)通
//        Esim esim = Esim.builder().esimId(2L).esimSupplierId(2l).build();
        // 電信
//        Esim esim = Esim.builder().esimId(3L).esimSupplierId(3l).build();

        EsimSupplier esimSupplier = esimSupplierFactory.getEsimSupplier(esim.getEsimSupplierId());

        esimSupplier.info(esim);
        esimSupplier.stop(esim);
        esimSupplier.reply(esim);
        esimSupplier.recharge(esim);
    }

}

6.2 測試結(jié)果

中國移動


image

中國聯(lián)通


image

中國電信


image

7. 拓展新供應(yīng)商

拓展新的供應(yīng)商只需要兩個步驟荧降,第一個步驟:添加配置接箫,第二個步驟:實現(xiàn)供應(yīng)商接口,具體如下朵诫。

7.1 添加配置文件

image

7.2 實現(xiàn)供應(yīng)商接口

繼承EsimSupplier辛友,重寫抽象類里的方法即可。


image

8. EsimSupplier的實現(xiàn)類中使用SpringBean方法

http://www.reibang.com/p/8b3864623a42

9.之后學(xué)到的更優(yōu)雅的辦法

9.1 供應(yīng)商實現(xiàn)類都加上@Component剪返,注冊為SpringBean

9.2 創(chuàng)建獲取SpringBean的工具類

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (SpringUtil.applicationContext == null) {
            SpringUtil.applicationContext = applicationContext;
        }
    }

    //獲取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通過name獲取 Bean.
    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    //通過class獲取Bean.
    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

    //通過name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name, Class<T> clazz) {
        return applicationContext.getBean(name, clazz);
    }

    //通過Class返回指定的BeanMap
    public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {
        return applicationContext.getBeansOfType(clazz);
    }
}

9.3 創(chuàng)建工廠類

import vip.gnloypp.test.svc.config.SpringUtil;
import vip.gnloypp.test.svc.service.EsimSupplier;

import java.util.Map;

public class EsimSupplierFactory {

    // 獲取所有 EsimSupplier 的實現(xiàn)類
    private static Map<String, EsimSupplier> beansMap;
    static {
        // 通過SpringBean工具類獲取所有 EsimSupplier 的實現(xiàn)類
        beansMap = SpringUtil.getBeansOfType(EsimSupplier.class);
    }

    // 根據(jù)實現(xiàn)類beanName獲取實現(xiàn)類
    public static EsimSupplier getEsimSupplier(String esimSupplierName) {
        EsimSupplier esimSupplier = beansMap.get(esimSupplierName);
        if(esimSupplier == null){
            throw new RuntimeException("供應(yīng)商不存在");
        }
        return esimSupplier;
    }
}

9.4 查詢esim卡信息獲取相應(yīng)的供應(yīng)商beanName废累,得到供應(yīng)商實現(xiàn)類

EsimSupplier supplierYiDong= EsimSupplierFactory.getEsimSupplier("supplierYiDong");
EsimSupplier supplierDianXin= EsimSupplierFactory.getEsimSupplier("supplierDianXin");
EsimSupplier supplierLianTong= EsimSupplierFactory.getEsimSupplier("supplierLianTong");
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邓梅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子邑滨,更是在濱河造成了極大的恐慌日缨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掖看,死亡現(xiàn)場離奇詭異殿遂,居然都是意外死亡,警方通過查閱死者的電腦和手機乙各,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門墨礁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人耳峦,你說我怎么就攤上這事恩静。” “怎么了蹲坷?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵驶乾,是天一觀的道長。 經(jīng)常有香客問我循签,道長级乐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任县匠,我火速辦了婚禮风科,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乞旦。我一直安慰自己贼穆,他們只是感情好,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布兰粉。 她就那樣靜靜地躺著故痊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪玖姑。 梳的紋絲不亂的頭發(fā)上愕秫,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機與錄音焰络,去河邊找鬼戴甩。 笑死,一個胖子當著我的面吹牛舔琅,可吹牛的內(nèi)容都是我干的等恐。 我是一名探鬼主播洲劣,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼备蚓,長吁一口氣:“原來是場噩夢啊……” “哼课蔬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起郊尝,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤二跋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后流昏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扎即,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年况凉,在試婚紗的時候發(fā)現(xiàn)自己被綠了谚鄙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡刁绒,死狀恐怖闷营,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情知市,我是刑警寧澤傻盟,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站嫂丙,受9級特大地震影響娘赴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜跟啤,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一诽表、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧隅肥,春花似錦关顷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捉片,卻和暖如春平痰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伍纫。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工宗雇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人莹规。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓赔蒲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子舞虱,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容