欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Spring中如何使用設(shè)計(jì)模式

 更新時(shí)間:2019年05月24日 09:44:47   作者:愛(ài)寶貝丶  
這篇文章主要介紹了淺談Spring中如何使用設(shè)計(jì)模式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

關(guān)于設(shè)計(jì)模式,如果使用得當(dāng),將會(huì)使我們的代碼更加簡(jiǎn)潔,并且更具擴(kuò)展性。本文主要講解Spring中如何使用策略模式,工廠方法模式以及Builder模式。

1. 策略模式

關(guān)于策略模式的使用方式,在Spring中其實(shí)比較簡(jiǎn)單,從本質(zhì)上講,策略模式就是一個(gè)接口下有多個(gè)實(shí)現(xiàn)類,而每種實(shí)現(xiàn)類會(huì)處理某一種情況。我們以發(fā)獎(jiǎng)勵(lì)為例進(jìn)行講解,比如我們?cè)诔楠?jiǎng)系統(tǒng)中,有多種獎(jiǎng)勵(lì)方式可供選擇,比如積分,虛擬幣和現(xiàn)金等。在存儲(chǔ)時(shí),我們必然會(huì)使用一個(gè)類似于type的字段用于表征這幾種發(fā)放獎(jiǎng)勵(lì)的,那么這里我們就可以使用多態(tài)的方式進(jìn)行獎(jiǎng)勵(lì)的發(fā)放。比如我們抽象出一個(gè) PrizeSender 的接口,其聲明如下:

public interface PrizeSender {

 /**
  * 用于判斷當(dāng)前實(shí)例是否支持當(dāng)前獎(jiǎng)勵(lì)的發(fā)放
  */
 boolean support(SendPrizeRequest request);

 /**
  * 發(fā)放獎(jiǎng)勵(lì)
  */
 void sendPrize(SendPrizeRequest request);

}

該接口中主要有兩個(gè)方法:support()和sendPrize(),其中support()方法主要用于判斷各個(gè)子類是否支持當(dāng)前類型數(shù)據(jù)的處理,而sendPrize()則主要是用于進(jìn)行具體的業(yè)務(wù)處理的,比如這里獎(jiǎng)勵(lì)的發(fā)放。下面就是我們?nèi)N不同類型的獎(jiǎng)勵(lì)發(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()方法中通過(guò)request的某個(gè)參數(shù)來(lái)控制當(dāng)前request是否是當(dāng)前實(shí)例能夠處理的類型,如果是,則外層的控制邏輯就會(huì)將request交給當(dāng)前實(shí)例進(jìn)行處理。關(guān)于這個(gè)類的設(shè)計(jì),有幾個(gè)點(diǎn)需要注意:

  1. 使用 @Component 注解對(duì)當(dāng)前類進(jìn)行標(biāo)注,將其聲明為Spring容器所管理的一個(gè)bean;
  2. 聲明一個(gè)返回boolean值的類似于 support() 的方法,通過(guò)這個(gè)方法來(lái)控制當(dāng)前實(shí)例是否為處理目標(biāo)request的實(shí)例;
  3. 聲明一個(gè)類似于 sendPrize() 的方法用于處理業(yè)務(wù)邏輯,當(dāng)然根據(jù)各個(gè)業(yè)務(wù)的不同聲明的方法名肯定是不同的,這里只是一個(gè)對(duì)統(tǒng)一的業(yè)務(wù)處理的抽象;
  4. 無(wú)論是 support() 方法還是 sendPrize() 方法,都需要傳一個(gè)對(duì)象進(jìn)行,而不是簡(jiǎn)簡(jiǎn)單單的基本類型的變量,這樣做的好處是后續(xù)如果要在Request中新增字段,那么就不需要修改接口的定義和已經(jīng)實(shí)現(xiàn)的各個(gè)子類的邏輯;

2. 工廠方法模式

上面我們講解了如何使用Spring來(lái)聲明一個(gè)策略模式,那么如何為不同的業(yè)務(wù)邏輯來(lái)注入不同的bean呢,或者說(shuō)外層的控制邏輯是什么樣的,這里我們就可以使用工廠方法模式了。所謂的工廠方法模式,就是定義一個(gè)工廠方法,通過(guò)傳入的參數(shù),返回某個(gè)實(shí)例,然后通過(guò)該實(shí)例來(lái)處理后續(xù)的業(yè)務(wù)邏輯。一般的,工廠方法的返回值類型是一個(gè)接口類型,而選擇具體子類實(shí)例的邏輯則封裝到了工廠方法中了。通過(guò)這種方式,來(lái)將外層調(diào)用邏輯與具體的子類的獲取邏輯進(jìn)行分離。如下圖展示了工廠方法模式的一個(gè)示意圖:

可以看到,工廠方法將具體實(shí)例的選擇進(jìn)行了封裝,而客戶端,也就是我們的調(diào)用方只需要調(diào)用工廠的具體方法獲取到具體的事例即可,而不需要管具體的實(shí)例實(shí)現(xiàn)是什么。上面我們講解了Spring中是如何使用策略模式聲明處理邏輯的,而沒(méi)有講如何選擇具體的策略,這里我們就可以使用工廠方法模式。如下是我們聲明的一個(gè) 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);
 }
}

這里我們聲明一個(gè)了一個(gè)工廠方法 getPrizeSender() ,其入?yún)⒕褪?SendPrizeRequest ,而返回值是某個(gè)實(shí)現(xiàn)了 PrizeSender 接口的實(shí)例,可以看到,通過(guò)這種方式,我們將具體的選擇方式下移到了具體的子類中的,因?yàn)楫?dāng)前實(shí)現(xiàn)了 PrizeSender 的bean是否支持當(dāng)前request的處理,是由具體的子類實(shí)現(xiàn)的。在該工廠方法中,我們也沒(méi)有任何與具體子類相關(guān)的邏輯,也就是說(shuō),該類實(shí)際上是可以動(dòng)態(tài)檢測(cè)新加入的子類實(shí)例的。這主要是通過(guò)Spring的自動(dòng)注入來(lái)實(shí)現(xiàn)的,主要是因?yàn)槲覀冞@里注入的是一個(gè) List<PrizeSender> ,也就是說(shuō),如果有新的 PrizeSender 的子類實(shí)例,只要其是Spring所管理的,那么都會(huì)被注入到這里來(lái)。下面就是我們編寫的一段用于測(cè)試的代碼來(lái)模擬調(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ù)庫(kù)或外部調(diào)用來(lái)生成的
  PrizeSender prizeSender = prizeSenderFactory.getPrizeSender(request);
  prizeSender.sendPrize(request);
 }
}

在客戶端代碼中,首先通過(guò) PrizeSenderFactory 獲取一個(gè) PrizeSender 實(shí)例,然后通過(guò)其 sendPrize() 方法發(fā)放具體的獎(jiǎng)勵(lì),通過(guò)這種方式,將具體的獎(jiǎng)勵(lì)發(fā)放邏輯與客戶端調(diào)用進(jìn)行了解耦。而且根據(jù)前面的講解,我們也知道,如果新增了一種獎(jiǎng)勵(lì)方式,我們只需要聲明一個(gè)新的實(shí)現(xiàn)了 PrizeSender 的bean即可,而不需要對(duì)現(xiàn)有代碼進(jìn)行任何修改。

3. Builder模式

關(guān)于Builder模式,我想使用過(guò)lombok的同學(xué)肯定會(huì)說(shuō)builder模式非常的簡(jiǎn)單,只需要在某個(gè)bean上使用 @Builder 注解進(jìn)行聲明即可,lombok可以自動(dòng)幫我們將其聲明為一個(gè)Builder的bean。關(guān)于這種使用方式,本人不置可否,不過(guò)就我的理解,這里主要有兩個(gè)點(diǎn)我們需要理解:

  • Builder模式就其名稱而言,是一個(gè)構(gòu)建者,我更傾向于將其理解為通過(guò)一定的參數(shù),通過(guò)一定的業(yè)務(wù)邏輯來(lái)最終生成某個(gè)對(duì)象。如果僅僅只是使用lombok的這種方式,其本質(zhì)上也還是創(chuàng)建了一個(gè)簡(jiǎn)單的bean,這個(gè)與通過(guò)getter和setter方式構(gòu)建一個(gè)bean是沒(méi)有什么大的區(qū)別的;
  • 在Spring框架中,使用設(shè)計(jì)模式最大的問(wèn)題在于如果在各個(gè)模式bean中能夠注入Spring的bean,如果能夠注入,那么將大大的擴(kuò)展其使用方式。因?yàn)槲覀兙涂梢哉娴膶?shí)現(xiàn)通過(guò)傳入的簡(jiǎn)單的幾個(gè)參數(shù),然后結(jié)合Spring注入的bean進(jìn)行一定的處理后,以構(gòu)造出我們所需要的某個(gè)bean。顯然,這是lombok所無(wú)法實(shí)現(xiàn)的;

關(guān)于Builder模式,我們可以以前面獎(jiǎng)勵(lì)發(fā)放的 SendPrizeRequest 的構(gòu)造為例進(jìn)行講解。在構(gòu)造request對(duì)象的時(shí)候,必然是通過(guò)前臺(tái)傳如的某些參數(shù)來(lái)經(jīng)過(guò)一定的處理,最后生成一個(gè)request對(duì)象。那么我們就可以使用Builder模式來(lái)構(gòu)建一個(gè) SendPrizeRequest 。這里假設(shè)根據(jù)前臺(tái)調(diào)用,我們能夠獲取到prizeId和userId,那么我們就可以創(chuàng)建一個(gè)如下的 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維護(hù)一個(gè)Builder模式的示例,具體的 維護(hù)方式就是在Builder類上使用 @Component 和 @Scope 注解來(lái)標(biāo)注該Builder類,這樣我們就可以在Builder類中注入我們所需要的實(shí)例來(lái)進(jìn)行一定的業(yè)務(wù)處理了。關(guān)于該模式,這里有幾點(diǎn)需要說(shuō)明:

  • 在Builder類上必須使用 @Scope 注解來(lái)標(biāo)注該實(shí)例為 prototype 類型,因?yàn)楹苊黠@,我們這里的Builder實(shí)例是有狀態(tài)的,無(wú)法被多線程共享;
  • 在Builder.build()方法中,我們可以通過(guò)傳入的參數(shù)和注入的bean來(lái)進(jìn)行一定的業(yè)務(wù)處理,從而得到構(gòu)建一個(gè) SendPrizeRequest 所需要的參數(shù);
  • Builder類必須使用static修飾,因?yàn)樵贘ava中,如果內(nèi)部類不用static修飾,那么該類的實(shí)例必須依賴于外部類的一個(gè)實(shí)例,而我們這里本質(zhì)上是希望通過(guò)內(nèi)部類實(shí)例來(lái)構(gòu)建外部類實(shí)例,也就是說(shuō)內(nèi)部類實(shí)例存在的時(shí)候,外部類實(shí)例是還不存在的,因而這里必須使用static修飾;
  • 根據(jù)標(biāo)準(zhǔn)的Builder模式的使用方式,外部類的各個(gè)參數(shù)都必須使用final修飾,然后只需要為其聲明getter方法即可。

上面我們展示了如何使用Spring的方式來(lái)聲明一個(gè)Builder模式的類,那么我們?cè)撊绾芜M(jìn)行使用呢,如下是我們的一個(gè)使用示例:

@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中,如果一個(gè)類是多例類型,也即使用 @Scope("prototype") 進(jìn)行了標(biāo)注,那么每次獲取該bean的時(shí)候就必須使用 ApplicationContext.getBean() 方法獲取一個(gè)新的實(shí)例,至于具體的原因,讀者可查閱相關(guān)文檔。我們這里就是通過(guò)一個(gè)單獨(dú)的方法來(lái)創(chuàng)建一個(gè)Builder對(duì)象,然后通過(guò) 流式 來(lái)為其設(shè)置prizeId和userId等參數(shù),最后通過(guò)build()方法構(gòu)建得到了一個(gè) SendPrizeRequest 實(shí)例,通過(guò)該實(shí)例來(lái)進(jìn)行后續(xù)的獎(jiǎng)勵(lì)發(fā)放。

4. 小結(jié)

本文主要通過(guò)一個(gè)獎(jiǎng)勵(lì)發(fā)放的示例來(lái)對(duì)Spring中如何使用工廠方法模式,策略模式和Builder模式的方式進(jìn)行講解,并且著重強(qiáng)調(diào)了實(shí)現(xiàn)各個(gè)模式時(shí)我們所需要注意的點(diǎn)。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 深入解析Java編程中的StringBuffer與StringBuider

    深入解析Java編程中的StringBuffer與StringBuider

    這篇文章主要介紹了Java編程中的StringBuffer與StringBuider,是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • 關(guān)于ApplicationContext的三個(gè)常用實(shí)現(xiàn)類

    關(guān)于ApplicationContext的三個(gè)常用實(shí)現(xiàn)類

    這篇文章主要介紹了關(guān)于ApplicationContext的三個(gè)常用實(shí)現(xiàn)類,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Java Mybatis框架增刪查改與核心配置詳解流程與用法

    Java Mybatis框架增刪查改與核心配置詳解流程與用法

    MyBatis 是一款優(yōu)秀的持久層框架,它支持自定義 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作。MyBatis 可以通過(guò)簡(jiǎn)單的 XML 或注解來(lái)配置和映射原始類型、接口和 Java POJO為數(shù)據(jù)庫(kù)中的記錄
    2021-10-10
  • 使用Java反射模擬實(shí)現(xiàn)Spring的IoC容器的操作

    使用Java反射模擬實(shí)現(xiàn)Spring的IoC容器的操作

    這篇文章主要介紹了使用Java反射模擬實(shí)現(xiàn)Spring的IoC容器的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java線程中start和run方法全面解析

    Java線程中start和run方法全面解析

    這篇文章主要介紹了Java線程中start和run方法的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-08-08
  • 關(guān)于后端如何解決跨域的問(wèn)題說(shuō)明

    關(guān)于后端如何解決跨域的問(wèn)題說(shuō)明

    這篇文章主要介紹了關(guān)于后端如何解決跨域的問(wèn)題說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • java中文傳值亂碼問(wèn)題的解決方法

    java中文傳值亂碼問(wèn)題的解決方法

    這篇文章主要為大家詳細(xì)介紹了java中文傳值亂碼問(wèn)題的解決方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Java使用ffmpeg和mencoder實(shí)現(xiàn)視頻轉(zhuǎn)碼

    Java使用ffmpeg和mencoder實(shí)現(xiàn)視頻轉(zhuǎn)碼

    這篇文章主要為大家詳細(xì)介紹了Java使用ffmpeg和mencoder實(shí)現(xiàn)視頻轉(zhuǎn)碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • 淺談Java中類的實(shí)例化步驟

    淺談Java中類的實(shí)例化步驟

    今天小編就為大家分享一篇關(guān)于淺談Java中類的實(shí)例化步驟,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02
  • Springmvc模式上傳和下載與enctype對(duì)比

    Springmvc模式上傳和下載與enctype對(duì)比

    這篇文章主要介紹了Springmvc模式上傳和下載與enctype對(duì)比,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12

最新評(píng)論