主頁 > 軟體設計 > 設計模式之裝飾器模式

設計模式之裝飾器模式

2022-08-11 12:55:00 軟體設計

本文由老王將建好的書房計劃請小王來幫忙,小王卻想謀權篡位,老王通過教育他引出裝飾器設計模式,第二部分針對老王提出的建設性意見實作裝飾器模式,第三部分針對裝飾器模式在Jdk中的IO、Spring中的快取管理器、Mybatis的運用來加強我們的理解,第四部分說明裝飾器模式和代理模式的區別及他們各自的應用場景,

讀者可以拉取完整代碼到本地進行學習,實作代碼均測驗通過后上傳到碼云,

一、引出問題

上篇文章對老王的書架改造以后,老王是相當的滿意,看小王能力突出,這不老王又有了新的需求,

經過組合模式以后老王的書被管理的井井有條,但是隨著書的增多,老王就有一些忙不過來了,老王就想讓小王幫他處理一些額外的事,比如在買書之前打掃一下書房,在晚上的時候把書房的門鎖一下;或者有人借書之前做一下記錄,借書者還書以后小王接收一下,等等,

小王聽完說這有何難,說完擼起袖子就準備改老王的代碼,老王急忙攔住了他,你真是個呆瓜,我寫的代碼你憑什么要動,你改了會不會影響我的業務邏輯,平時讓你多看書你不聽,之前學的設計模式呢?不拿出來用,眼看著讓他吃灰,

小王不好意思的撓撓頭,翻出來了他的設計模式寶典,開始尋找合適的設計模式,

小王大喊有了,之前說過的代理模式可以很好的解決這個問題,代理模式可以動態的增強物件的一些特性,我準備使用代理模式完成這個需求,

老王聽完止不住的搖搖頭,看來你是打算謀權篡位了,你是想要我整個書房的權利呀!

老王解釋說,代理模式是可以實作這個需求,但是在這個場景下顯然代理模式不合適,代理模式是著重對物件的控制,而我們今天的需求是在該物件的基礎之上增加他的一些功能,我們各自的業務獨立發展互不干擾,

二、裝飾器模式概念與使用

實際上,在原物件的基礎之上增加其功能就是屬于裝飾器模式,

裝飾器模式(Decorator Pattern)允許向一個現有的物件添加新的功能,同時又不改變其結構,這種型別的設計模式屬于結構型模式,它是作為現有的類的一個包裝,

在裝飾器模式中應該是有四個角色:

①Component抽象構件(老王抽象方法)
②ConcreteComponent 具體構件(老王實作方法)
③Decorator裝飾角色(裝飾者小王)
④ConcreteDecorator 具體裝飾角色(裝飾者小王實作方法)

在裝飾器模式中,需要增強的類(被裝飾者)要實作介面,裝飾者繼承被裝飾者的介面,并將被裝飾者的實體傳進去,在具體裝飾角色中呼叫被裝飾者的方法,在其前后定義增強的方法,在實際應用中往往裝飾角色和具體裝飾角色合二為一,

我們看下具體的代碼實作:

抽象構件:

/**
 * 書的抽象構件
 * @author tcy
 * @Date 10-08-2022
 */
public abstract class ComponentBook {

    /**
     * 借書
     */
    public abstract void borrowBook();

    /**
     * 買書
     */
    public abstract void buyBook();

}

書的具體構件:

/**
 * 書的具體構件
 * @author tcy
 * @Date 10-08-2022
 */
public class ConcreteComponentBook extends ComponentBook{
    @Override
    public void borrowBook() {
        System.out.println("老王的書借出去...");

    }

    @Override
    public void buyBook() {
        System.out.println("老王的書買回來...");

    }
}

裝飾角色:

/**
 * 書的裝飾者
 * @author tcy
 * @Date 10-08-2022
 */
public class DecoratorBook extends ComponentBook{

    private ComponentBook componentBook;

    DecoratorBook(ComponentBook componentBook){
        this.componentBook=componentBook;
    }

    @Override
    public void borrowBook() {
        this.componentBook.borrowBook();
    }

    @Override
    public void buyBook() {
        this.componentBook.buyBook();
    }
}

書的具體裝飾角色:

/**
 * 子類里寫了并且使用了無參的構造方法但是它的父類(祖先)中卻至少有一個是沒有無參構造方法的
 * @author tcy
 * @Date 10-08-2022
 */
public class ConcreteDecoratorBook1 extends DecoratorBook{

    ConcreteDecoratorBook1(ComponentBook componentBook) {
        super(componentBook);
    }

    public void cleanRoom(){
        System.out.println("打掃書房...");
    }

    public void shutRoom(){
        System.out.println("關閉書房...");
    }

    public void recordBook(){
        System.out.println("記錄借出記錄...");
    }

    public void returnBook(){
        System.out.println("收到借出去的書...");
    }

    @Override
    public void buyBook() {
        this.cleanRoom();
        super.buyBook();
        this.shutRoom();
        System.out.println("----------------------------");
    }

    @Override
    public void borrowBook() {
        this.recordBook();
        super.borrowBook();
        this.returnBook();
        System.out.println("----------------------------");
    }
}

如果讀者的Java基礎扎實,理解裝飾器還是比較輕松的,裝飾器的實作方式很直觀,需要特別指出的是,在書的具體裝飾角色中,要顯示的定義一個構造方法,

基礎不太扎實的讀者可能會有一個疑問,在Java的類中默認不是會有一個無參的構造方法嗎?為什么這里還需要定義呢?

在java中一個類只要有父類,那么在它實體化的時候,一定是從頂級的父類開始創建,

也就是說當你用子類的無參建構式創建子類物件時,會去先遞回呼叫父類的無參構造方法,這時候如果某個類的父類沒有無參構造方法就會編譯出差,所以我們在子類中可以手動定義一個無參方法,或者在父類中顯示的定義一個構造方法,

客戶端:

/**
 * @author tcy
 * @Date 09-08-2022
 */
public class  {
    public static void main(String[] args) {

        ComponentBook componentBook=new ConcreteComponentBook();
        componentBook=new ConcreteDecoratorBook1(componentBook);

        componentBook.borrowBook();

        componentBook.buyBook();
    }
}

方法呼叫后我們可以看到執行結果:

記錄借出記錄...
老王的書借出去...
收到借出去的書...
----------------------------
打掃書房...
老王的書買回來...
關閉書房...
----------------------------

在老王借書和買書的這個事件中,成功的織入進去小王的方法,這樣也就實作了裝飾器模式,

為了加強理解我們接著看裝飾器模式在我們經常接觸的原始碼中的運用,

三、應用

1、jdk中的應用IO

裝飾器在java中最典型的應用就是IO,我們知道在IO家族中有各種各樣的流,而流往往都是作用在子類之上,然后增加其附加功能,我們以InputStream 舉例,

InputStream 是位元組輸入流,此抽象類是表示位元組輸入流的所有類的超類,

FileInputStream是InputStream 的一個實作父類,BufferedInputStream是FileInputStream的實作父類,

實際BufferedInputStream就是裝飾者,InputStream 就是抽象構件,FileInputStream是具體構件,BufferedInputStream就是對FileInputStream進行了包裝,

我們看具體的應用:

FileInputStream fileInputStream = new FileInputStream(filePath); 
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

直接將FileInputStream的實體傳給BufferedInputStream的構造方法,就能呼叫BufferedInputStream增強的一些方法了,

我們具體看BufferedInputStream裝飾器類:

public class BufferedInputStream extends FilterInputStream {

    private static int DEFAULT_BUFFER_SIZE = 8192;

    protected int marklimit;

    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

  	...
    //增強的方法
    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer */
        else if (pos >= buffer.length)  /* no room left in buffer */
            if (markpos > 0) {  /* can throw away early part of the buffer */
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
            } else if (buffer.length >= marklimit) {
                markpos = -1;   /* buffer got too big, invalidate mark */
                pos = 0;        /* drop buffer contents */
            } else if (buffer.length >= MAX_BUFFER_SIZE) {
                throw new OutOfMemoryError("Required array size too large");
            } else {            /* grow buffer */
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    // Can't replace buf if there was an async close.
                    // Note: This would need to be changed if fill()
                    // is ever made accessible to multiple threads.
                    // But for now, the only way CAS can fail is via close.
                    // assert buf == null;
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
            }
        count = pos;
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }

   ...

BufferedInputStream類中有許多其他的方法,就是對FileInputStream類的增強,

2、Spring中的運用

Spring使用裝飾器模式有兩個典型的特征,一個是類名中含有Wrapper,另一類是含有Decorator,功能也即動態的給某些類增加一些額外的功能,

TransactionAwareCacheDecorator是處理spring有事務的時候快取的類,我們在使用spring的cache注解實作快取的時候,當出現事務的時候,那么快取的同步性就需要做相應的處理了,于是就有了這個裝飾者,

public class TransactionAwareCacheDecorator implements Cache {

	//抽象構件
   private final Cache targetCache;

   /**
    * Create a new TransactionAwareCache for the given target Cache.
    * @param targetCache the target Cache to decorate
    */
   public TransactionAwareCacheDecorator(Cache targetCache) {
      Assert.notNull(targetCache, "Target Cache must not be null");
      this.targetCache = targetCache;
   }

   /**直接呼叫未增強
    * Return the target Cache that this Cache should delegate to.
    */
   public Cache getTargetCache() {
      return this.targetCache;
   }

	//直接呼叫未增強
   @Override
   public String getName() {
      return this.targetCache.getName();
   }

//直接呼叫未增強
   @Override
   public Object getNativeCache() {
      return this.targetCache.getNativeCache();
   }

//直接呼叫未增強
   @Override
   @Nullable
   public ValueWrapper get(Object key) {
      return this.targetCache.get(key);
   }

//直接呼叫未增強
   @Override
   public <T> T get(Object key, @Nullable Class<T> type) {
      return this.targetCache.get(key, type);
   }

//直接呼叫未增強
   @Override
   @Nullable
   public <T> T get(Object key, Callable<T> valueLoader) {
      return this.targetCache.get(key, valueLoader);
   }

//先進行判斷確定是否需要增強
   @Override
   public void put(final Object key, @Nullable final Object value) {
      if (TransactionSynchronizationManager.isSynchronizationActive()) {
         TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
               TransactionAwareCacheDecorator.this.targetCache.put(key, value);
            }
         });
      }
      else {
         this.targetCache.put(key, value);
      }
   }
}

Cache是抽象構件,TransactionAwareCacheDecorator就是裝飾者,而Cache的實作類就是具體構件,

image-20220810114927963

因為并非所有的方法都會使用事務,有的普通方法就不需要裝飾,有的就需要,所以就使用了裝飾者模式來完成,

比如put()方法:

  @Override
   public void put(final Object key, @Nullable final Object value) {
      if (TransactionSynchronizationManager.isSynchronizationActive()) {
         TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
               TransactionAwareCacheDecorator.this.targetCache.put(key, value);
            }
         });
      }
      else {
         this.targetCache.put(key, value);
      }
   }

會在put前判斷是否開啟了事務TransactionSynchronizationManager.isSynchronizationActive(),如果開啟事務就呼叫一下額外的方法,如果沒有開始事務就呼叫默認的方法,

我們舉的這個例子呼叫的就是 TransactionSynchronizationManager.registerSynchronization()方法,也即是為當前執行緒注冊一個新的事務同步,

在Spring中將裝飾角色和具體裝飾角色合二為一,直接在裝飾者中實作要增加的方法,

3、MyBatista的運用

了解過MyBatis的大致執行流程的讀者應該知道,Executor是MyBatis執行器,是MyBatis 調度的核心,負責SQL陳述句的生成和查詢快取的維護;CachingExecutor是一個Executor的裝飾器,給一個Executor增加了快取的功能,此時可以看做是對Executor類的一個增強,故使用裝飾器模式是合適的,

我們首先看下Executor類的繼承結構,

image-20220810113008409

我們將關鍵的CachingExecutor代碼放上:

public class CachingExecutor implements Executor {
    //持有組件物件
  private Executor delegate;
  private TransactionalCacheManager tcm = new TransactionalCacheManager();
    //構造方法,傳入組件物件
  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }
  @Override
  public int update(MappedStatement ms, Object parameterObject) throws SQLException {
      //轉發請求給組件物件,可以在轉發前后執行一些附加動作
    flushCacheIfRequired(ms);
    return delegate.update(ms, parameterObject);
  }
  //...
 }

Executor就是抽象構件,BaseExecutor是具體構件的實作,CachingExecutor就是裝飾角色,那具體裝飾角色在哪呢?

實際中具體裝飾角色直接在裝飾角色中集成了,并沒有將具體裝飾角色完全獨立出來,

另外,Mybatis的一級快取和二級快取也是使用的裝飾者模式,有興趣的讀者可以拉取Mybatis的源代碼本地進行除錯研究,

四、總結

到此為止,我們就將裝飾器模式的內容講解清楚了,看到這讀者可能發現,針對某一類需求可能會有很多設計模式都能完成需求,但一定是有最合適的那一個,就像我們今天舉的例子無論是用裝飾器模式還是代理模式都可以實作這個需求,

但我們看代理模式中我們列舉的例子是以租房做例子,中介將房子的權利完全移交過去,中介完全控制房子做一些改造,今天書房的需求只是讓小王來幫忙的,還是以老王為主體,小王只是做一些附加,

裝飾器模式就是在瓶里面插了一朵花,而代理模式是把瓶子都給人家了,讓人家隨便折騰,

如果我們的需求是日志收集、攔截器,代理模式是最適合的,如果是動態的增加物件的功能、限制物件的執行條件、引數控制和檢查等使用配接器模式就更加合適了,

推薦讀者,參考軟體設計七大原則 認真閱讀往期的文章,認真體會,

創建型設計模式

一、設計模式之工廠方法和抽象工廠

二、設計模式之單例和原型

三、設計模式之建造者模式

結構型設計模式

四、設計模式之代理模式

五、設計模式之配接器模式

六、橋接模式

七、組合模式

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/501542.html

標籤:設計模式

上一篇:微服務介面設計原則

下一篇:設計模式之裝飾器模式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more