您好,登錄后才能下訂單哦!
這篇文章主要介紹了Spring中使用設(shè)計模式的案例,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
關(guān)于設(shè)計模式,如果使用得當(dāng),將會使我們的代碼更加簡潔,并且更具擴展性。本文主要講解Spring中如何使用策略模式,工廠方法模式以及Builder模式。
1. 策略模式
關(guān)于策略模式的使用方式,在Spring中其實比較簡單,從本質(zhì)上講,策略模式就是一個接口下有多個實現(xiàn)類,而每種實現(xiàn)類會處理某一種情況。我們以發(fā)獎勵為例進行講解,比如我們在抽獎系統(tǒng)中,有多種獎勵方式可供選擇,比如積分,虛擬幣和現(xiàn)金等。在存儲時,我們必然會使用一個類似于type的字段用于表征這幾種發(fā)放獎勵的,那么這里我們就可以使用多態(tài)的方式進行獎勵的發(fā)放。比如我們抽象出一個 PrizeSender 的接口,其聲明如下:
public interface PrizeSender { /** * 用于判斷當(dāng)前實例是否支持當(dāng)前獎勵的發(fā)放 */ boolean support(SendPrizeRequest request); /** * 發(fā)放獎勵 */ void sendPrize(SendPrizeRequest request); }
該接口中主要有兩個方法:support()和sendPrize(),其中support()方法主要用于判斷各個子類是否支持當(dāng)前類型數(shù)據(jù)的處理,而sendPrize()則主要是用于進行具體的業(yè)務(wù)處理的,比如這里獎勵的發(fā)放。下面就是我們?nèi)N不同類型的獎勵發(fā)放的具體代碼:
// 積分發(fā)放 @Component public class PointSender implements PrizeSender { @Override public boolean support(SendPrizeRequest request) { return request.getPrizeType() == PrizeTypeEnum.POINT; } @Override public void sendPrize(SendPrizeRequest request) { System.out.println("發(fā)放積分"); } }
// 虛擬幣發(fā)放 @Component public class VirtualCurrencySender implements PrizeSender { @Override public boolean support(SendPrizeRequest request) { return PrizeTypeEnum.VIRTUAL_CURRENCY == request.getPrizeType(); } @Override public void sendPrize(SendPrizeRequest request) { System.out.println("發(fā)放虛擬幣"); } }
// 現(xiàn)金發(fā)放 @Component public class CashSender implements PrizeSender { @Override public boolean support(SendPrizeRequest request) { return PrizeTypeEnum.CASH == request.getPrizeType(); } @Override public void sendPrize(SendPrizeRequest request) { System.out.println("發(fā)放現(xiàn)金"); } }
這里可以看到,在每種子類型中,我們只需要在support()方法中通過request的某個參數(shù)來控制當(dāng)前request是否是當(dāng)前實例能夠處理的類型,如果是,則外層的控制邏輯就會將request交給當(dāng)前實例進行處理。關(guān)于這個類的設(shè)計,有幾個點需要注意:
使用 @Component 注解對當(dāng)前類進行標(biāo)注,將其聲明為Spring容器所管理的一個bean;
聲明一個返回boolean值的類似于 support() 的方法,通過這個方法來控制當(dāng)前實例是否為處理目標(biāo)request的實例;
聲明一個類似于 sendPrize() 的方法用于處理業(yè)務(wù)邏輯,當(dāng)然根據(jù)各個業(yè)務(wù)的不同聲明的方法名肯定是不同的,這里只是一個對統(tǒng)一的業(yè)務(wù)處理的抽象;
無論是 support() 方法還是 sendPrize() 方法,都需要傳一個對象進行,而不是簡簡單單的基本類型的變量,這樣做的好處是后續(xù)如果要在Request中新增字段,那么就不需要修改接口的定義和已經(jīng)實現(xiàn)的各個子類的邏輯;
2. 工廠方法模式
上面我們講解了如何使用Spring來聲明一個策略模式,那么如何為不同的業(yè)務(wù)邏輯來注入不同的bean呢,或者說外層的控制邏輯是什么樣的,這里我們就可以使用工廠方法模式了。所謂的工廠方法模式,就是定義一個工廠方法,通過傳入的參數(shù),返回某個實例,然后通過該實例來處理后續(xù)的業(yè)務(wù)邏輯。一般的,工廠方法的返回值類型是一個接口類型,而選擇具體子類實例的邏輯則封裝到了工廠方法中了。通過這種方式,來將外層調(diào)用邏輯與具體的子類的獲取邏輯進行分離。如下圖展示了工廠方法模式的一個示意圖:
可以看到,工廠方法將具體實例的選擇進行了封裝,而客戶端,也就是我們的調(diào)用方只需要調(diào)用工廠的具體方法獲取到具體的事例即可,而不需要管具體的實例實現(xiàn)是什么。上面我們講解了Spring中是如何使用策略模式聲明處理邏輯的,而沒有講如何選擇具體的策略,這里我們就可以使用工廠方法模式。如下是我們聲明的一個 PrizeSenderFactory :
@Component public class PrizeSenderFactory { @Autowired private List<PrizeSender> prizeSenders; public PrizeSender getPrizeSender(SendPrizeRequest request) { for (PrizeSender prizeSender : prizeSenders) { if (prizeSender.support(request)) { return prizeSender; } } throw new UnsupportedOperationException("unsupported request: " + request); } }
這里我們聲明一個了一個工廠方法 getPrizeSender() ,其入?yún)⒕褪?SendPrizeRequest ,而返回值是某個實現(xiàn)了 PrizeSender 接口的實例,可以看到,通過這種方式,我們將具體的選擇方式下移到了具體的子類中的,因為當(dāng)前實現(xiàn)了 PrizeSender 的bean是否支持當(dāng)前request的處理,是由具體的子類實現(xiàn)的。在該工廠方法中,我們也沒有任何與具體子類相關(guān)的邏輯,也就是說,該類實際上是可以動態(tài)檢測新加入的子類實例的。這主要是通過Spring的自動注入來實現(xiàn)的,主要是因為我們這里注入的是一個 List<PrizeSender> ,也就是說,如果有新的 PrizeSender 的子類實例,只要其是Spring所管理的,那么都會被注入到這里來。下面就是我們編寫的一段用于測試的代碼來模擬調(diào)用方的調(diào)用:
@Service public class ApplicationService { @Autowired private PrizeSenderFactory prizeSenderFactory; public void mockedClient() { SendPrizeRequest request = new SendPrizeRequest(); request.setPrizeType(PrizeTypeEnum.POINT); // 這里的request一般是根據(jù)數(shù)據(jù)庫或外部調(diào)用來生成的 PrizeSender prizeSender = prizeSenderFactory.getPrizeSender(request); prizeSender.sendPrize(request); } }
在客戶端代碼中,首先通過 PrizeSenderFactory 獲取一個 PrizeSender 實例,然后通過其 sendPrize() 方法發(fā)放具體的獎勵,通過這種方式,將具體的獎勵發(fā)放邏輯與客戶端調(diào)用進行了解耦。而且根據(jù)前面的講解,我們也知道,如果新增了一種獎勵方式,我們只需要聲明一個新的實現(xiàn)了 PrizeSender 的bean即可,而不需要對現(xiàn)有代碼進行任何修改。
3. Builder模式
關(guān)于Builder模式,我想使用過lombok的同學(xué)肯定會說builder模式非常的簡單,只需要在某個bean上使用 @Builder 注解進行聲明即可,lombok可以自動幫我們將其聲明為一個Builder的bean。關(guān)于這種使用方式,本人不置可否,不過就我的理解,這里主要有兩個點我們需要理解:
Builder模式就其名稱而言,是一個構(gòu)建者,我更傾向于將其理解為通過一定的參數(shù),通過一定的業(yè)務(wù)邏輯來最終生成某個對象。如果僅僅只是使用lombok的這種方式,其本質(zhì)上也還是創(chuàng)建了一個簡單的bean,這個與通過getter和setter方式構(gòu)建一個bean是沒有什么大的區(qū)別的;
在Spring框架中,使用設(shè)計模式最大的問題在于如果在各個模式bean中能夠注入Spring的bean,如果能夠注入,那么將大大的擴展其使用方式。因為我們就可以真的實現(xiàn)通過傳入的簡單的幾個參數(shù),然后結(jié)合Spring注入的bean進行一定的處理后,以構(gòu)造出我們所需要的某個bean。顯然,這是lombok所無法實現(xiàn)的;
關(guān)于Builder模式,我們可以以前面獎勵發(fā)放的 SendPrizeRequest 的構(gòu)造為例進行講解。在構(gòu)造request對象的時候,必然是通過前臺傳如的某些參數(shù)來經(jīng)過一定的處理,最后生成一個request對象。那么我們就可以使用Builder模式來構(gòu)建一個 SendPrizeRequest 。這里假設(shè)根據(jù)前臺調(diào)用,我們能夠獲取到prizeId和userId,那么我們就可以創(chuàng)建一個如下的 SendPrizeRequest :
public class SendPrizeRequest { private final PrizeTypeEnum prizeType; private final int amount; private final String userId; public SendPrizeRequest(PrizeTypeEnum prizeType, int amount, String userId) { this.prizeType = prizeType; this.amount = amount; this.userId = userId; } @Component @Scope("prototype") public static class Builder { @Autowired PrizeService prizeService; private int prizeId; private String userId; public Builder prizeId(int prizeId) { this.prizeId = prizeId; return this; } public Builder userId(String userId) { this.userId = userId; return this; } public SendPrizeRequest build() { Prize prize = prizeService.findById(prizeId); return new SendPrizeRequest(prize.getPrizeType(), prize.getAmount(), userId); } } public PrizeTypeEnum getPrizeType() { return prizeType; } public int getAmount() { return amount; } public String getUserId() { return userId; } }
這里就是使用Spring維護一個Builder模式的示例,具體的 維護方式就是在Builder類上使用 @Component 和 @Scope 注解來標(biāo)注該Builder類,這樣我們就可以在Builder類中注入我們所需要的實例來進行一定的業(yè)務(wù)處理了。關(guān)于該模式,這里有幾點需要說明:
在Builder類上必須使用 @Scope 注解來標(biāo)注該實例為 prototype 類型,因為很明顯,我們這里的Builder實例是有狀態(tài)的,無法被多線程共享;
在Builder.build()方法中,我們可以通過傳入的參數(shù)和注入的bean來進行一定的業(yè)務(wù)處理,從而得到構(gòu)建一個 SendPrizeRequest 所需要的參數(shù);
Builder類必須使用static修飾,因為在Java中,如果內(nèi)部類不用static修飾,那么該類的實例必須依賴于外部類的一個實例,而我們這里本質(zhì)上是希望通過內(nèi)部類實例來構(gòu)建外部類實例,也就是說內(nèi)部類實例存在的時候,外部類實例是還不存在的,因而這里必須使用static修飾;
根據(jù)標(biāo)準(zhǔn)的Builder模式的使用方式,外部類的各個參數(shù)都必須使用final修飾,然后只需要為其聲明getter方法即可。
上面我們展示了如何使用Spring的方式來聲明一個Builder模式的類,那么我們該如何進行使用呢,如下是我們的一個使用示例:
@Service public class ApplicationService { @Autowired private PrizeSenderFactory prizeSenderFactory; @Autowired private ApplicationContext context; public void mockedClient() { SendPrizeRequest request = newPrizeSendRequestBuilder() .prizeId(1) .userId("u4352234") .build(); PrizeSender prizeSender = prizeSenderFactory.getPrizeSender(request); prizeSender.sendPrize(request); } public Builder newPrizeSendRequestBuilder() { return context.getBean(Builder.class); } }
上述代碼中,我們主要要看一下 newPrizeSendRequestBuilder() 方法,在Spring中,如果一個類是多例類型,也即使用 @Scope("prototype") 進行了標(biāo)注,那么每次獲取該bean的時候就必須使用 ApplicationContext.getBean() 方法獲取一個新的實例,至于具體的原因,讀者可查閱相關(guān)文檔。我們這里就是通過一個單獨的方法來創(chuàng)建一個Builder對象,然后通過 流式 來為其設(shè)置prizeId和userId等參數(shù),最后通過build()方法構(gòu)建得到了一個 SendPrizeRequest 實例,通過該實例來進行后續(xù)的獎勵發(fā)放。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Spring中使用設(shè)計模式的案例”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。