App下載

使用 Spring 實(shí)現(xiàn)策略模式原來(lái)可以這么簡(jiǎn)單!

猿友 2020-09-25 10:32:49 瀏覽數(shù) (4804)
反饋

文章來(lái)源于Python極客技術(shù) 作者:鴨血粉絲

最近看同事的代碼時(shí)候,學(xué)到了個(gè)小技巧,在某些場(chǎng)景下非常挺有用的,這里分享一下給大家。

Spring@Autowired注解,大家應(yīng)該不會(huì)陌生,用過(guò) Spring 的肯定也離不開(kāi)這個(gè)注解,通過(guò)這個(gè)注解可以幫我們自動(dòng)注入我們想要的 Bean。

除了這個(gè)基本功能之外,@Autowired 還有更加強(qiáng)大的功能,還可以注入指定類(lèi)型的數(shù)組,List/Set 集合,甚至還可以是 Map 對(duì)象。

比如說(shuō)當(dāng)前應(yīng)用有一個(gè)支付接口 PayService,分別需要對(duì)接支付寶、微信支付、銀行卡,所以分別有三個(gè)不同實(shí)現(xiàn)類(lèi) AliPayService,WechatPayservice,BankCardPayService。

四個(gè)類(lèi)的關(guān)系如下圖所示:

四個(gè)類(lèi)的關(guān)系

如果此時(shí)我需要獲取當(dāng)前系統(tǒng)類(lèi)所有 PayService Bean,老的方式我們只能通過(guò) BeanFactory或者 ApplicationContext 獲取。

// 首先通過(guò) getBeanNamesForType 獲取 PayService 類(lèi)型所有的 Bean
String[] names = ctx.getBeanNamesForType(PayService.class);
List anotherPayService = Lists.newArrayList();
for (String beanName : names) {
    anotherPayService.add(ctx.getBean(beanName, PayService.class));
}
// 或者通過(guò) getBeansOfType 獲取所有 PayService 類(lèi)型
Map beansOfType = ctx.getBeansOfType(PayService.class);
for (Map.Entry entry : beansOfType.entrySet()) {
    anotherPayService.add(entry.getValue());
}

但是現(xiàn)在我們可以不用這么麻煩了,我們可以直接使用 @Autowired 注入 PayService Bean 數(shù)組,或者 PayService List/Set 集合,甚至,我們還可以注入 PayService 的 Map 集合。

@Autowired
List payServices;


@Autowired
PayService[] payServicesArray;

Spring 實(shí)現(xiàn)策略模式

知道了這個(gè)功能,當(dāng)我們需要使用 Spring 實(shí)現(xiàn)策略模式就非常簡(jiǎn)單。

可能有的小伙伴不太了解策略模式,沒(méi)關(guān)系,這類(lèi)阿粉介紹一個(gè)業(yè)務(wù)場(chǎng)景,通過(guò)這個(gè)場(chǎng)景給大家介紹一下策略模式。

還是上面的例子,我們當(dāng)前系統(tǒng)需要對(duì)接微信支付、支付寶、以及銀行卡支付。

當(dāng)接到這個(gè)需求,我們首先需要拿到相應(yīng)接口文檔,分析三者的共性。

假設(shè)我們這里發(fā)現(xiàn),三者模式比較類(lèi)似,只是部分傳參不一樣。

所以我們根據(jù)三者的共性,抽象出一組公共的接口 PayService,

public interface PayService {
    PayResult epay(PayRequest request);
}

然后分別實(shí)現(xiàn)三個(gè)實(shí)現(xiàn)類(lèi),都繼承這個(gè)接口。

那么現(xiàn)在問(wèn)題來(lái)了,由于存在三個(gè)實(shí)現(xiàn)類(lèi),如何選擇具體的實(shí)現(xiàn)類(lèi)?

其實(shí)這個(gè)問(wèn)題很好解決,請(qǐng)求參數(shù)傳入一個(gè)唯一標(biāo)識(shí),然后我們根據(jù)標(biāo)識(shí)選擇相應(yīng)的實(shí)現(xiàn)類(lèi)。

比如說(shuō)我們?cè)谡?qǐng)求類(lèi) PayRequest 搞個(gè) channelNo 字段,這個(gè)代表相應(yīng)支付渠道唯一標(biāo)識(shí),比如說(shuō)支付寶為:00000001,微信支付為 00000002,銀行卡支付為 00000003。

接著我們需要把唯一標(biāo)識(shí)與具體實(shí)現(xiàn)類(lèi)一一映射起來(lái),剛好我們可以使用 Map 存儲(chǔ)這種映射關(guān)系。

我們實(shí)現(xiàn)一個(gè) RouteService,具體代碼邏輯如下:

@Service
public class RouteService {


    @Autowired
    Map payServiceMap;


    public PayResult epay(PayRequest payRequest) {
        PayService payService = payServiceMap.get(payRequest.getChannelNo());
        return  payService.epay(payRequest);
    }


}

我們?cè)?RouteService 自動(dòng)注入 PayService 所有相關(guān) Bean,然后使用唯一標(biāo)識(shí)查找實(shí)現(xiàn)類(lèi)。

這樣我們對(duì)外就屏蔽了支付渠道的差異,其他服務(wù)類(lèi)只要調(diào)用 RouteService 即可。

但是這樣實(shí)現(xiàn)還是有點(diǎn)小問(wèn)題,由于我們唯一標(biāo)識(shí)為一串?dāng)?shù)字,如果像我們上面直接使用 @Autowired注入 Map,這就需要我們實(shí)現(xiàn)類(lèi)的 Bean 名字為 00000001 這些。

但是這樣命名不是很優(yōu)雅,這樣會(huì)讓后來(lái)同學(xué)很難看懂,不好維護(hù)。

所以我們需要做個(gè)轉(zhuǎn)換,我們可以這么實(shí)現(xiàn)。

首先我們改造一下 PayService 這個(gè)接口,增加一個(gè)方法,每個(gè)具體實(shí)現(xiàn)類(lèi)通過(guò)這個(gè)方法返回其唯一標(biāo)識(shí)。

public interface PayService {


    PayResult epay(PayRequest request);


    String channel();
}

具體舉個(gè)支付寶實(shí)現(xiàn)類(lèi)的代碼,其他實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)類(lèi)似。

@Service("aliPayService")
public class AliPayService implements PayService {


    @Override
    public PayResult epay(PayRequest request) {
        // 業(yè)務(wù)邏輯
        return new PayResult();
    }
    @Override
    public String channel() {
        return "00000001";
    }
}

最后我們改造一下 RouteService,具體邏輯如下:

@Service
public class RouteService {


    @Autowired
    Set payServiceSet;

    
    Map payServiceMap;


    public PayResult epay(PayRequest payRequest) {
        PayService payService = payServiceMap.get(payRequest.getChannelNo());
        return  payService.epay(payRequest);
    }


    @PostConstruct
    public void init() {
        for (PayService payService : payServiceSet) {
            payServiceMap = new HashMap();
            payServiceMap.put(payService.channel(), payService);
        }
    }
}

上面代碼首先通過(guò)自動(dòng)注入 PayService 一個(gè)集合,然后我們?cè)賹⑵滢D(zhuǎn)為一個(gè) Map,這樣內(nèi)部存儲(chǔ)剛好是唯一標(biāo)識(shí)與實(shí)現(xiàn)類(lèi)的映射了。

以上就是W3Cschool編程獅關(guān)于使用 Spring 實(shí)現(xiàn)策略模式原來(lái)可以這么簡(jiǎn)單!的相關(guān)介紹了,希望對(duì)大家有所幫助。

0 人點(diǎn)贊