關於設計模式,若是使用得當,將會使咱們的代碼更加簡潔,而且更具擴展性。本文主要講解Spring中如何使用策略模式,工廠方法模式以及Builder模式。java
關於策略模式的使用方式,在Spring中其實比較簡單,從本質上講,策略模式就是一個接口下有多個實現類,而每種實現類會處理某一種狀況。咱們以發獎勵爲例進行講解,好比咱們在抽獎系統中,有多種獎勵方式可供選擇,好比積分,虛擬幣和現金等。在存儲時,咱們必然會使用一個相似於type的字段用於表徵這幾種發放獎勵的,那麼這裏咱們就可使用多態的方式進行獎勵的發放。好比咱們抽象出一個PrizeSender
的接口,其聲明以下:數據庫
public interface PrizeSender { /** * 用於判斷當前實例是否支持當前獎勵的發放 */ boolean support(SendPrizeRequest request); /** * 發放獎勵 */ void sendPrize(SendPrizeRequest request); }
該接口中主要有兩個方法:support()和sendPrize(),其中support()方法主要用於判斷各個子類是否支持當前類型數據的處理,而sendPrize()則主要是用於進行具體的業務處理的,好比這裏獎勵的發放。下面就是咱們三種不一樣類型的獎勵發放的具體代碼:設計模式
// 積分發放 @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("發放積分"); } }
// 虛擬幣發放 @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("發放虛擬幣"); } }
// 現金髮放 @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("發放現金"); } }
這裏能夠看到,在每種子類型中,咱們只須要在support()方法中經過request的某個參數來控制當前request是不是當前實例可以處理的類型,若是是,則外層的控制邏輯就會將request交給當前實例進行處理。關於這個類的設計,有幾個點須要注意:多線程
@Component
註解對當前類進行標註,將其聲明爲Spring容器所管理的一個bean;support()
的方法,經過這個方法來控制當前實例是否爲處理目標request的實例;sendPrize()
的方法用於處理業務邏輯,固然根據各個業務的不一樣聲明的方法名確定是不一樣的,這裏只是一個對統一的業務處理的抽象;support()
方法仍是sendPrize()
方法,都須要傳一個對象進行,而不是簡簡單單的基本類型的變量,這樣作的好處是後續若是要在Request中新增字段,那麼就不須要修改接口的定義和已經實現的各個子類的邏輯;上面咱們講解了如何使用Spring來聲明一個策略模式,那麼如何爲不一樣的業務邏輯來注入不一樣的bean呢,或者說外層的控制邏輯是什麼樣的,這裏咱們就可使用工廠方法模式了。所謂的工廠方法模式,就是定義一個工廠方法,經過傳入的參數,返回某個實例,而後經過該實例來處理後續的業務邏輯。通常的,工廠方法的返回值類型是一個接口類型,而選擇具體子類實例的邏輯則封裝到了工廠方法中了。經過這種方式,來將外層調用邏輯與具體的子類的獲取邏輯進行分離。以下圖展現了工廠方法模式的一個示意圖:框架
能夠看到,工廠方法將具體實例的選擇進行了封裝,而客戶端,也就是咱們的調用方只須要調用工廠的具體方法獲取到具體的事例便可,而不須要管具體的實例實現是什麼。上面咱們講解了Spring中是如何使用策略模式聲明處理邏輯的,而沒有講如何選擇具體的策略,這裏咱們就可使用工廠方法模式。以下是咱們聲明的一個PrizeSenderFactory
:ide
@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()
,其入參就是SendPrizeRequest
,而返回值是某個實現了PrizeSender
接口的實例,能夠看到,經過這種方式,咱們將具體的選擇方式下移到了具體的子類中的,由於當前實現了PrizeSender
的bean是否支持當前request的處理,是由具體的子類實現的。在該工廠方法中,咱們也沒有任何與具體子類相關的邏輯,也就是說,該類其實是能夠動態檢測新加入的子類實例的。這主要是經過Spring的自動注入來實現的,主要是由於咱們這裏注入的是一個List<PrizeSender>
,也就是說,若是有新的PrizeSender
的子類實例,只要其是Spring所管理的,那麼都會被注入到這裏來。下面就是咱們編寫的一段用於測試的代碼來模擬調用方的調用:測試
@Service public class ApplicationService { @Autowired private PrizeSenderFactory prizeSenderFactory; public void mockedClient() { SendPrizeRequest request = new SendPrizeRequest(); request.setPrizeType(PrizeTypeEnum.POINT); // 這裏的request通常是根據數據庫或外部調用來生成的 PrizeSender prizeSender = prizeSenderFactory.getPrizeSender(request); prizeSender.sendPrize(request); } }
在客戶端代碼中,首先經過PrizeSenderFactory
獲取一個PrizeSender
實例,而後經過其sendPrize()
方法發放具體的獎勵,經過這種方式,將具體的獎勵發放邏輯與客戶端調用進行了解耦。並且根據前面的講解,咱們也知道,若是新增了一種獎勵方式,咱們只須要聲明一個新的實現了PrizeSender
的bean便可,而不須要對現有代碼進行任何修改。ui
關於Builder模式,我想使用過lombok的同窗確定會說builder模式很是的簡單,只須要在某個bean上使用@Builder
註解進行聲明便可,lombok能夠自動幫咱們將其聲明爲一個Builder的bean。關於這種使用方式,本人不置能否,不過就個人理解,這裏主要有兩個點咱們須要理解:this
關於Builder模式,咱們能夠之前面獎勵發放的SendPrizeRequest
的構造爲例進行講解。在構造request對象的時候,必然是經過前臺傳如的某些參數來通過必定的處理,最後生成一個request對象。那麼咱們就可使用Builder模式來構建一個SendPrizeRequest
。這裏假設根據前臺調用,咱們可以獲取到prizeId和userId,那麼咱們就能夠建立一個以下的SendPrizeRequest
:prototype
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
註解來標註該Builder類,這樣咱們就能夠在Builder類中注入咱們所須要的實例來進行必定的業務處理了。關於該模式,這裏有幾點須要說明:
@Scope
註解來標註該實例爲prototype
類型,由於很明顯,咱們這裏的Builder實例是有狀態的,沒法被多線程共享;SendPrizeRequest
所須要的參數;上面咱們展現瞭如何使用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")
進行了標註,那麼每次獲取該bean的時候就必須使用ApplicationContext.getBean()
方法獲取一個新的實例,至於具體的緣由,讀者可查閱相關文檔。咱們這裏就是經過一個單獨的方法來建立一個Builder對象,而後經過流式來爲其設置prizeId和userId等參數,最後經過build()方法構建獲得了一個SendPrizeRequest
實例,經過該實例來進行後續的獎勵發放。
本文主要經過一個獎勵發放的示例來對Spring中如何使用工廠方法模式,策略模式和Builder模式的方式進行講解,而且着重強調了實現各個模式時咱們所須要注意的點。