1. 程式人生 > >看透設計模式-結構型模式

看透設計模式-結構型模式

 這裡我們主要討論 結構型模式

介面卡模式,:

● Target(目標抽象類):目標抽象類定義客戶所需介面,可以是一個抽象類或介面,也可以是具體類

● Adapter(介面卡類):介面卡可以呼叫另一個介面,作為一個轉換器,對Adaptee和Target進行適配,介面卡類是介面卡模式的核心,在物件介面卡中,它通過繼承Target並關聯一個Adaptee物件使二者產生聯絡。

● Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經存在的介面,這個介面需要適配,適配者類一般是一個具體類,包含了客戶希望使用的業務方法,在某些情況下可能沒有適配者類的原始碼。

注意上圖的 Adapter、Adaptee 是關聯關係,實際上,我們也可以做成 繼承關係,注意,因為java不支援多繼承,所以這個時候,Adapter和Target 的關係必須不能是 繼承了,一般就是 實現介面的關係。

上面兩種都是單向適配,就是說,只能通過 Target 訪問 Adaptee 的功能, 而不能反過來。 實際上,我們有時候可能需要雙向適配:

還有 預設介面卡:

 

介面卡模式, 簡單說就是 將沒有繼承實現關係的 但又有些近似、相同的兩個功能,通過一個adapter整合到一起。

適用範圍:將沒有繼承實現關係的 但又有些近似、相同的兩個功能,通過一個adapter整合到一起。  (當然,如果 差別很大,那也沒什麼必要非要適配到一起了)

優缺點:

由於java不支援多繼承,所以我們的adapter不能適配多個適配者(這個其實也不是什麼大問題)

適配者類不能為最終類,如在Java中不能為final類

 類介面卡模式中的目標抽象類只能為介面,不能為類,其使用有一定的侷限性。

討論:

Target怎麼理解,就是我們需要去適配的目標!client 通過 Target 來訪問,而Adaptee 是 對Target 的適配,Adaptee  物件介面卡模式中,不能是介面,必須是具體類。Adapter 顯然必須是 具體類。

實際情況,往往復雜而且難以捉摸。 我們設計模式討論的是最簡單的 類的結構、行為。那麼,Target 可以存在其他需要適配的介面嗎? 當然,也是可以有的,這個時候呢,我們需要一個Adapter + 多個Adaptee  ,Adapter 實現了多個適配方法。

拓展性:Target和Adapter 之間存在繼承關係,所以我們容易拓展多個 Adapter 。 但是實際上,我們真的需要嗎? 不好說

 

代理模式:

(1) Subject(抽象主題角色):它聲明瞭真實主題和代理主題的共同介面,這樣一來在任何使用真實主題的地方都可以使用代理主題,客戶端通常需要針對抽象主題角色進行程式設計。

(2) Proxy(代理主題角色):它包含了對真實主題的引用,從而可以在任何時候操作真實主題物件;在代理主題角色中提供一個與真實主題角色相同的介面,以便在任何時候都可以替代真實主題;代理主題角色還可以控制對真實主題的使用,負責在需要的時候建立和刪除真實主題物件,並對真實主題物件的使用加以約束。通常,在代理主題角色中,客戶端在呼叫所引用的真實主題操作之前或之後還需要執行其他操作,而不僅僅是單純呼叫真實主題物件中的操作。

(3) RealSubject(真實主題角色):它定義了代理角色所代表的真實物件,在真實主題角色中實現了真實的業務操作,客戶端可以通過代理主題角色間接呼叫真實主題角色中定義的操作。

 

拓展性:上面的圖,我們可以看到, 存在兩個 繼承關係,那麼 我們當然很容易對我們的主題進行各種代理,但是注意到 Proxy 存在一個RealSubject引用, 所以,Proxy、RealSubject 是成對出現的。
討論:

1 Subject 是必須的嗎?我認為Subject、RealSubject可以合併到一起。 當然,複雜一些的情況,我們可能需要多個RealSubject,這樣Subject 也是不可少的。

2 Proxy 可以代理 多個RealSubject嗎?不太好,雖然我們可以給Proxy 多個realSubject 引用,比如realSubject1, realSubject2 分別對應兩個真實的 主題,但是我們只有一個 request 方法,不方便一個request方法內部 代理 處理兩個  主題。

3 RealSubject可以 使用多個 Proxy 代理嗎? 完全沒問題。

4 我們可以給 Subject 設定多個方法嗎? 比如 request1,request2,表示 即代理這個事情,又那個事情。 理論上是可以的。但是畢竟麻煩,不如使用多個Subject更好?

5 如果我想代理的RealSubject沒有實現某介面,換句話說,不存在 Subject (這也是常常 看到的 實際情況), 怎麼做代理? 那我們可以省去 Subject, 或者採取其他的一些代理方式。

 

代理 這個詞的使用場景非常非常多,其語義範疇也是十分的廣泛。

分類: 虛擬~、遠端~、保護~、cache~、防火牆~、同步化~、智慧引用~、copy-on-write~

在實際開發過程中,代理類的實現比上述程式碼要複雜很多,代理模式根據其目的和實現方式不同可分為很多種類,其中常用的幾種代理模式簡要說明如下:

       (1) 遠端代理(Remote Proxy):為一個位於不同的地址空間的物件提供一個本地的代理物件,這個不同的地址空間可以是在同一臺主機中,也可是在另一臺主機中,遠端代理又稱為大使(Ambassador)。

       (2) 虛擬代理(Virtual Proxy):如果需要建立一個資源消耗較大的物件,先建立一個消耗相對較小的物件來表示,真實物件只在需要時才會被真正建立。

       (3) 保護代理(Protect Proxy):控制對一個物件的訪問,可以給不同的使用者提供不同級別的使用許可權。

       (4) 緩衝代理(Cache Proxy):為某一個目標操作的結果提供臨時的儲存空間,以便多個客戶端可以共享這些結果。

       (5) 智慧引用代理(Smart Reference Proxy):當一個物件被引用時,提供一些額外的操作,例如將物件被呼叫的次數記錄下來等。

在這些常用的代理模式中,有些代理類的設計非常複雜,例如遠端代理類,它封裝了底層網路通訊和對遠端物件的呼叫,其實現較為複雜。。

討論:

代理模式是容易理解的,也就 兩個繼承關係+ 一個關聯關係而已吧。 但是按照場景(按代理類的preRequest、postRequest 的具體工作的不同)細分下來,又變得複雜起來了,這麼多分類,也很難記住,有時候也不是叫做這個名稱,不必花任何時間去記憶, 記住了就記住了,記不住就算了。

 

裝飾模式:

● Component(抽象構件):它是具體構件和抽象裝飾類的共同父類,聲明瞭在具體構件中實現的業務方法,它的引入可以使客戶端以一致的方式處理未被裝飾的物件以及裝飾之後的物件,實現客戶端的透明操作。

● ConcreteComponent(具體構件):它是抽象構件類的子類,用於定義具體的構件物件,實現了在抽象構件中宣告的方法,裝飾器可以給它增加額外的職責(方法)。

● Decorator(抽象裝飾類):它也是抽象構件類的子類,用於給具體構件增加職責,但是具體職責在其子類中實現。它維護一個指向抽象構件物件的引用,通過該引用可以呼叫裝飾之前構件物件的方法,並通過其子類擴充套件該方法,以達到裝飾的目的。

● ConcreteDecorator(具體裝飾類):它是抽象裝飾類的子類,負責向構件新增新的職責。每一個具體裝飾類都定義了一些新的行為,它可以呼叫在抽象裝飾類中定義的方法,並可以增加新的方法用以擴充物件的行為。

      由於具體構件類和裝飾類都實現了相同的抽象構件介面,因此裝飾模式以對客戶透明的方式動態地給一個物件附加上更多的責任,換言之,客戶端並不會覺得物件在裝飾前和裝飾後有什麼不同。裝飾模式可以在不需要創造更多子類的情況下,將物件的功能加以擴充套件。

上面的uml 有些複雜。裝飾模式的核心在於抽象裝飾類的設計

可以多次的裝飾,就是巢狀,或者說wrapper,從而實現更加複雜的功能。java io 的Filter流,可以 動態 增減 功能。

 在實際使用過程中,由於新增行為可能需要單獨呼叫,因此這種形式的裝飾模式也經常出現,這種裝飾模式被稱為半透明(Semi-transparent)裝飾模式,而標準的裝飾模式是透明(Transparent)裝飾模式。

(1) 儘量保持裝飾類的介面與被裝飾類的介面相同,這樣,對於客戶端而言,無論是裝飾之前的物件還是裝飾之後的物件都可以一致對待。這也就是說,在可能的情況下,我們應該儘量使用透明裝飾模式。

(2) 儘量保持具體構件類ConcreteComponent是一個“輕”類,也就是說不要把太多的行為放在具體構件類中,我們可以通過裝飾類對其進行擴充套件。

(3) 如果只有一個具體構件類,那麼抽象裝飾類可以作為該具體構件類的直接子類。
簡化:--------------------- 

Component 是否可以是 介面? 我認為 理論上也是可以的,但是,介面 一般不宜繼續做 封裝,故裝飾模式不適合介面。

裝飾模式的關鍵就是, 我們需要 Decarator 繼承 Component(或 ConcreteComponent)

適用場景:在不影響其他物件的情況下,以動態、透明的方式給單個物件新增職責。

拓展性:  雖然存在3個實現關係,但是,關鍵的恐怕還是 ConcreteDecorator 對 Decorator  的實現關係。

討論: 

1 Decorator 是可以省去的嗎? 我認為完全可以省去,但是如果需要多個 ConcreteComponent, 那麼Decorator 自然不能去掉。

2 我們看到了,裝飾模式很重要一點是需要繼承Component,那麼它和繼承有什麼大的區別,ConcreteDecorator 直接繼承Component或 ConcreteComponent是不是就好了?為什麼要多此一舉?  直接繼承自然是可以 進行 功能拓展的,但是, 如果 需要靈活的 新增各種功能, 那麼 繼承類的 個數會 急劇上升。 而使用 Decorator 模式的 優點也正式如此:

(1) 對於擴充套件一個物件的功能,裝飾模式比繼承更加靈活性,不會導致類的個數急劇增加。

(2) 可以通過一種動態的方式來擴充套件一個物件的功能,通過配置檔案可以在執行時選擇不同的具體裝飾類,從而實現不同的行為。

(3) 可以對一個物件進行多次裝飾,通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行為的組合,得到功能更為強大的物件。

(4) 具體構件類與具體裝飾類可以獨立變化,使用者可以根據需要增加新的具體構件類和具體裝飾類,原有類庫程式碼無須改變,符合“開閉原則”。
--------------------- 

—— 特別需要注意的是 Decorator  不僅僅是繼承於 Component,而且它還聚合了一個 Component,Decorator  的主要功能仍然是Component 提供的,它只不過是 做了一些裝飾,這也是 裝飾模式 名稱的 由來吧~!

關於為什麼“裝飾模式比繼承更加靈活性,不會導致類的個數急劇增加” , 我們看看java io 的FilterStream 就知道了, 如果不這樣做,那麼各種流,不知道會有多少, 而且 用起來也是非常不方便,需要搞懂每個流的細微區別才行。

裝飾模式降低了系統的耦合度,可以動態增加或刪除物件的職責並使得需要裝飾的具體構件類和具體裝飾類可以獨立變化,以便增加新的具體構件類和具體裝飾類。在軟體開發中,裝飾模式應用較為廣泛,例如在JavaIO中的輸入流和輸出流的設計、javax.swing包中一些圖形介面構件功能的增強等地方都運用了裝飾模式
---------------------  

討論2:

裝飾模式 和 代理模式 非常相似,有一些細微的差別:

1 裝飾模式的 ConcreteDecorator 的 component 欄位 聚合Component,而不是ConcreteComponent, 這意味著 component 欄位 可以是繼續是一個ConcreteDecorator  ; Proxy 有一個 realSubject 欄位 關聯指向 RealSubject 而不是 Subject, 意味著不能在嵌套了,這是語法上的限制也是 語義上的 需求。  這一點就意味著 裝飾模式 更加靈活。

2 忽略字面含義,Subject 和 Component的 語義也是差別很大的。實際的Subject  可能不存在, 而 Component通常是存在的。

3 裝飾模式 傾向於 “動態的新增功能” (不會動態減少功能,不需要那麼不裝飾就是了,沒必要減少人家原本有的功能。。),前也好,後也好。。, 代理模式 常用於 在 RealSubject 實際工作的前後做一些 “不是很緊要的工作”

 

外觀模式:

為子系統中的一組介面提供一個統一的入口。外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。

(1) Facade(外觀角色):在客戶端可以呼叫它的方法,在外觀角色中可以知道相關的(一個或者多個)子系統的功能和責任;在正常情況下,它將所有從客戶端發來的請求委派到相應的子系統去,傳遞給相應的子系統物件處理。

      (2) SubSystem(子系統角色):在軟體系統中可以有一個或者多個子系統角色,每一個子系統可以不是一個單獨的類,而是一個類的集合,它實現子系統的功能;每一個子系統都可以被客戶端直接呼叫,或者被外觀角色呼叫,它處理由外觀類傳過來的請求;子系統並不知道外觀的存在,對於子系統而言,外觀角色僅僅是另外一個客戶端而已。
---------------------

在標準的外觀模式結構圖中,如果需要增加、刪除或更換與外觀類互動的子系統類,必須修改外觀類或客戶端的原始碼,這將違背開閉原則,因此可以通過引入抽象外觀類來對系統進行改進,在一定程度上可以解決該問題。—— 我認為非要這麼做,這個模式就不是 Facade 模式了!

如絕大多數B/S系統都有一個首頁或者導航頁面,大部分C/S系統都提供了選單或者工具欄,在這裡,首頁和導航頁面就是B/S系統的外觀角色,而選單和工具欄就是C/S系統的外觀角色,通過它們使用者可以快速訪問子系統,降低了系統的複雜程度。所有涉及到與多個業務物件互動的場景都可以考慮使用外觀模式進行重構。
---------------------  

拓展性:這個模式 是談不上拓展性的,一拓展子系統就 違背了 開閉原則。 

討論:

這個模式, 其實不算是真正的模式, 只能說是一個思想。

常見場景:B/S系統首頁、C/S系統的選單或者工具欄 ...

 

橋接模式:

橋接模式(Bridge Pattern) 將抽象部分與它的實現部分分離,使它們都可以獨立地變化。它是一種物件結構型模式,又稱為柄體(Handle and Body)模式或介面(Interface)模式。

●Abstraction(抽象類):用於定義抽象類的介面,它一般是抽象類而不是介面,其中定義了一個Implementor(實現類介面)型別的物件並可以維護該物件,它與Implementor之間具有關聯關係,它既可以包含抽象業務方法,也可以包含具體業務方法。

●RefinedAbstraction(擴充抽象類):擴充由Abstraction定義的介面,通常情況下它不再是抽象類而是具體類,它實現了在Abstraction中宣告的抽象業務方法,在RefinedAbstraction中可以呼叫在Implementor中定義的業務方法。

●Implementor(實現類介面):定義實現類的介面 (是個 介面!),這個介面不一定要與Abstraction的介面完全一致,事實上這兩個介面可以完全不同,一般而言,Implementor介面僅提供基本操作,而Abstraction定義的介面可能會做更多更復雜的操作。Implementor介面對這些基本操作進行了宣告,而具體實現交給其子類。通過關聯關係,在Abstraction中不僅擁有自己的方法,還可以呼叫到Implementor中定義的方法,使用關聯關係來替代繼承關係。

●ConcreteImplementor(具體實現類):具體實現Implementor介面,在不同的ConcreteImplementor中提供基本操作的不同實現,在程式執行時,ConcreteImplementor物件將替換其父類物件,提供給抽象類具體的業務操作方法。

這個模式有些難以理解,Abstraction,Implementor 的語義不太好,模式中 什麼是抽象部分,什麼是實現部分? 有明確的定義嗎?沒有吧,我覺得 不一定非要是 抽象和實現,Abstraction理解成 靜態的,Implementor 理解為 動態變化的部分是不是更好? 理解成 兩個維度是不是更好? 

(我認為, 上圖的 operation() 和 operationImpl() 其實可以沒有任何關係的,這樣的命名完全就是 誤導。 不需要,也不應該這樣命名。)

 

橋接模式總結
        橋接模式是設計Java虛擬機器和實現JDBC等驅動程式的核心模式之一,應用較為廣泛。在軟體開發中如果一個類或一個系統有多個變化維度時,都可以嘗試使用橋接模式對其進行設計。橋接模式為多維度變化的系統提供了一套完整的解決方案,並且降低了系統的複雜度。

1.主要優點

        橋接模式的主要優點如下:

        (1)分離抽象介面及其實現部分。橋接模式使用“物件間的關聯關係”解耦了抽象和實現之間固有的繫結關係,使得抽象和實現可以沿著各自的維度來變化。所謂抽象和實現沿著各自維度的變化,也就是說抽象和實現不再在同一個繼承層次結構中,而是“子類化”它們,使它們各自都具有自己的子類,以便任何組合子類,從而獲得多維度組合物件。

        (2)在很多情況下,橋接模式可以取代多層繼承方案,多層繼承方案違背了“單一職責原則”,複用性較差,且類的個數非常多,橋接模式是比多層繼承方案更好的解決方法,它極大減少了子類的個數。

        (3)橋接模式提高了系統的可擴充套件性,在兩個變化維度中任意擴充套件一個維度,都不需要修改原有系統,符合“開閉原則”。

2.主要缺點

        橋接模式的主要缺點如下:

        (1)橋接模式的使用會增加系統的理解與設計難度,由於關聯關係建立在抽象層,要求開發者一開始就針對抽象層進行設計與程式設計。

        (2)橋接模式 要求  正確識別出系統中兩個獨立變化的維度,因此其使用範圍具有一定的侷限性如何正確識別兩個獨立維度也需要一定的經驗積累
--------------------- 

適用場景

        在以下情況下可以考慮使用橋接模式:

        (1)如果一個系統需要在抽象化和具體化之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承關係,通過橋接模式可以使它們在抽象層建立一個關聯關係。

        (2) “抽象部分”和“實現部分”可以以繼承的方式獨立擴充套件而互不影響,在程式執行時可以動態將一個抽象化子類的物件和一個實現化子類的物件進行組合,即系統需要對抽象化角色和實現化角色進行動態耦合。

        (3)一個類存在兩個(或多個)獨立變化的維度,且這兩個(或多個)維度都需要獨立進行擴充套件

        (4)對於那些不希望使用繼承或因為多層繼承導致系統類的個數急劇增加的系統,橋接模式尤為適用。
---------------------  

如果系統中存在兩個以上的變化維度,是否可以使用橋接模式進行處理?如果可以,系統該如何設計? 我想自然是可以的,通過另外一個 impl 聚合另外一個Implementor2即可

 

介面卡模式與橋接模式的聯用

 

橋接的重點是  橋, 這裡的橋,其實就是一個 聚合關係。通過一座橋 就為原來的Abstraction 提供了一個Implementor。 一個Implementor 就是一個額外的維度,增加更加多的橋,我們實現了更多的維度的拓展。而且, 需要特別注意的是, 這裡的Implementor 就是一個額外的維度。是用加法實現乘法的拓展關係。 比如現在是3個維度,如果通過繼承, 我們需要 3*3 個類,如果是 橋接,那麼是 3+3。—— 從這點來看,橋接和 裝飾模式 有異曲同工之妙。

橋接和 裝飾模式 的區別呢?

橋接模式 是通過 一個 聚合關係 拓展了 一個 維度。

裝飾模式 是通過繼承+聚合,在原來各個方法上 增加了一些功能,主要的功能, 是沒有太多變化的!

簡化: 如果只有一個ConcreteImplementor的話,似乎可以去掉Implementor了,同理Abstraction。

拓展性:沒有裝飾模式的“繼承”約束,這個模式是很自由的,所以拓展性相當好。

討論:

Implementor 是否可以存在多個方法, 比如 operationImpl1 operationImpl2? 我認為完全是可以的,具體場景具體分析。 然而,如果Implementor 存在多個方法,那麼 把Implementor  拆分為多個介面,變成多個維度,是不是更好?

 

 

組合模式:

組合模式(Composite Pattern) 組合多個物件形成樹形結構以表示具有“整體—部分”關係的層次結構。組合模式對單個物件(即葉子物件)和組合物件(即容器物件)的使用具有一致性,組合模式又可以稱為“整體—部分”(Part-Whole)模式,它是一種物件結構型模式。

 

 

● Component(抽象構件):它可以是介面或抽象類,為葉子構件和容器構件物件宣告介面,在該角色中可以包含所有子類共有行為的宣告和實現。在抽象構件中定義了訪問及管理它的子構件的方法,如增加子構件、刪除子構件、獲取子構件等。

● Leaf(葉子構件):它在組合結構中表示葉子節點物件,葉子節點沒有子節點,它實現了在抽象構件中定義的行為。對於那些訪問及管理子構件的方法,可以通過異常等方式進行處理。

● Composite(容器構件):它在組合結構中表示容器節點物件,容器節點包含子節點,其子節點可以是葉子節點,也可以是容器節點,它提供一個集合用於儲存子節點,實現了在抽象構件中定義的行為,包括那些訪問及管理子構件的方法,在其業務方法中可以遞迴呼叫其子節點的業務方法。
---------------------

組合模式的關鍵是定義了一個抽象構件類,它既可以代表葉子,又可以代表容器,而客戶端針對該抽象構件類進行程式設計,無須知道它到底表示的是葉子還是容器,可以對其進行統一處理。同時容器物件與抽象構件類之間還建立一個聚合關聯關係在容器物件中既可以包含葉子,也可以包含容器,以此實現遞迴組合,形成一個樹形結構

      如果不使用組合模式,客戶端程式碼將過多地依賴於容器物件複雜的內部實現結構,容器物件內部實現結構的變化將引起客戶程式碼的頻繁變化,帶來了程式碼維護複雜、可擴充套件性差等弊端。組合模式的引入將在一定程度上解決這些問題。
---------------------  

關鍵其實就在於 元件容器的引入,使得存在一種靈活的“包含”的關係。 組合模式的 容器, 用到了 迭代器模式。

組合模式做一些變化,然後分類,可分為 透明組合模式與安全組合模式:

透明組合模式:

安全組合模式:

 

 

適用場景

      在以下情況下可以考慮使用組合模式:

      (1) 在具有整體和部分的層次結構中,希望通過一種方式忽略整體與部分的差異客戶端可以一致地對待它們

      (2) 在一個使用面嚮物件語言開發的系統中需要處理 一個樹形結構

      (3) 在一個系統中能夠分離出葉子物件和容器物件,而且它們的型別不固定,需要增加一些新的型別。
---------------------  

 Composite 和 Leaf 並沒有功能上的很大的區別,不過是Composite 是Leaf 的集合,提供了集合操作的功能而已。

 拓展性:uml存在兩個實現,那麼,新增Leaf 和 Composite 都是很方便的。

討論,組合模式關鍵就在於 Composite 的出現,使得它區別於一般的繼承,Composite 存在一個 集合 聚合了Component 本身,同時又提供了其他和Component  一模一樣的功能。所以,我們使用此模式的時候,關鍵就是說 是否存在這麼一個 Composite,組合Leaf, 統一的對外提供服務 。

 簡化:除去client還有3個關係,都不能去掉。所以,顯然已經無法再簡化了!

 

 

 享元模式:

在享元模式結構圖中包含如下幾個角色:

      ● Flyweight(抽象享元類):通常是一個介面或抽象類,在抽象享元類中聲明瞭具體享元類公共的方法,這些方法可以向外界提供享元物件的內部資料(內部狀態),同時也可以通過這些方法來設定外部資料(外部狀態)。

      ● ConcreteFlyweight(具體享元類):它實現了抽象享元類,其例項稱為享元物件;在具體享元類中為內部狀態提供了儲存空間。通常我們可以結合單例模式來設計具體享元類,為每一個具體享元類提供唯一的享元物件。

      ● UnsharedConcreteFlyweight(非共享具體享元類):並不是所有的抽象享元類的子類都需要被共享,不能被共享的子類可設計為非共享具體享元類;當需要一個非共享具體享元類的物件時可以直接通過例項化建立。

      ● FlyweightFactory(享元工廠類):享元工廠類用於建立並管理享元物件,它針對抽象享元類程式設計,將各種型別的具體享元物件儲存在一個享元池中,享元池一般設計為一個儲存“鍵值對”的集合(也可以是其他型別的集合),可以結合工廠模式進行設計;當用戶請求一個具體享元物件時,享元工廠提供一個儲存在享元池中已建立的例項或者建立一個新的例項(如果不存在的話),返回新建立的例項並將其儲存在享元池中。
--------------------- 

複合享元模式

       將一些單純享元物件使用組合模式加以組合,還可以形成複合享元物件,這樣的複合享元物件本身不能共享,但是它們可以分解成單純享元物件,而後者則可以共享

當系統中存在大量相同或者相似的物件時,享元模式是一種較好的解決方案,它通過共享技術實現相同或相似的細粒度物件的複用,從而節約了記憶體空間,提高了系統性能。相比其他結構型設計模式,享元模式的使用頻率並不算太高,但是作為一種以“節約記憶體,提高效能”為出發點的設計模式,它在軟體開發中還是得到了一定程度的應用。

       1.主要優點

       享元模式的主要優點如下:

       (1) 可以極大減少記憶體中物件的數量,使得相同或相似物件在記憶體中只儲存一份,從而可以節約系統資源,提高系統性能。

       (2) 享元模式的外部狀態相對獨立,而且不會影響其內部狀態,從而使得享元物件可以在不同的環境中被共享。

       2.主要缺點

       享元模式的主要缺點如下:

       (1) 享元模式使得系統變得複雜,需要分離出內部狀態和外部狀態,這使得程式的邏輯複雜化。

       (2) 為了使物件可以共享,享元模式需要將享元物件的部分狀態外部化,而讀取外部狀態將使得執行時間變長。

       3.適用場景

       在以下情況下可以考慮使用享元模式:

       (1) 一個系統有大量相同或者相似的物件,造成記憶體的大量耗費。

       (2) 物件的大部分狀態都可以外部化,可以將這些外部狀態傳入物件中

       (3) 在使用享元模式時需要維護一個儲存享元物件的享元池,而這需要耗費一定的系統資源,因此,應當在需要多次重複使用享元物件時才值得使用享元模式。
---------------------  

 元意味著 本質的 東西,更加細化的東西,擁有內部狀態的東西。享元其實可以理解 為 一個 池子, 比如 資料庫連線池、執行緒池,物件池。。。它其實更加簡單,它是一個非常簡單的池子,基本上就是 封裝了一個 HashMap, 然後把 HashMap的 get set 方法 合二為一。

 簡化:只有3個關係,都不能減少或改變。


本文大部分參考:https://blog.csdn.net/lovelion