設(shè)計(jì)模式之結(jié)構(gòu)型

設(shè)計(jì)模式概述

基于設(shè)計(jì)原則,GoF(設(shè)計(jì)模式總結(jié)4人組)總結(jié)了軟件開發(fā)領(lǐng)域的23個(gè)經(jīng)典設(shè)計(jì)模式。雖然GoF設(shè)計(jì)模式只有23個(gè),但是它們各具特色,每個(gè)模式都為某一個(gè)可重復(fù)的設(shè)計(jì)問題提供了一套解決方案。根據(jù)它們的用途,設(shè)計(jì)模式可分為創(chuàng)建型(Creational),結(jié)構(gòu)型(Structural)和行為型(Behavioral)三種,其中創(chuàng)建型模式主要用于描述如何創(chuàng)建對(duì)象,結(jié)構(gòu)型模式主要用于描述如何實(shí)現(xiàn)類或?qū)ο蟮慕M合,行為型模式主要用于描述類或?qū)ο笤鯓咏换ヒ约霸鯓臃峙渎氊?zé),在GoF 23種設(shè)計(jì)模式中包含5種創(chuàng)建型設(shè)計(jì)模式、7種結(jié)構(gòu)型設(shè)計(jì)模式和11種行為型設(shè)計(jì)模式。此外,根據(jù)某個(gè)模式主要是用于處理類之間的關(guān)系還是對(duì)象之間的關(guān)系,設(shè)計(jì)模式還可以分為類模式和對(duì)象模式。我們經(jīng)常將兩種分類方式結(jié)合使用,如單例模式是對(duì)象創(chuàng)建型模式,模板方法模式是類行為型模式。
值得一提的是,有一個(gè)設(shè)計(jì)模式雖然不屬于GoF 23種設(shè)計(jì)模式,但一般在介紹設(shè)計(jì)模式時(shí)都會(huì)對(duì)它進(jìn)行說明,它就是簡(jiǎn)單工廠模式,也許是太“簡(jiǎn)單”了,GoF并沒有把它寫到那本經(jīng)典著作中,不過現(xiàn)在大部分的設(shè)計(jì)模式書籍都會(huì)對(duì)它進(jìn)行專門的介紹。

                                 常用設(shè)計(jì)模式一覽表
類型 模式名稱 學(xué)習(xí)難度 使用頻率
創(chuàng)建型模式(Creational Pattern) 單例模式(Singleton Pattern) ★☆☆☆☆ ★★★★☆
創(chuàng)建型模式(Creational Pattern) 簡(jiǎn)單工廠模式(Simple Factory Pattern) ★★☆☆☆ ★★★☆☆
創(chuàng)建型模式(Creational Pattern) 工廠方法模式(Factory Method Pattern) ★★☆☆☆ ★★★★★
創(chuàng)建型模式(Creational Pattern) 抽象工廠模式(Abstract Factory Pattern) ★★★★☆ ★★★★★
創(chuàng)建型模式(Creational Pattern) 原型模式(Prototype Pattern) ★★★☆☆ ★★★☆☆
創(chuàng)建型模式(Creational Pattern) 建造者模式(Builder Pattern) ★★★★☆ ★★☆☆☆
結(jié)構(gòu)型模式(Structural Pattern) 適配器模式(Adapter Pattern) ★★☆☆☆ ★★★★☆
結(jié)構(gòu)型模式(Structural Pattern) 橋接模式(Bridge Pattern) ★★★☆☆ ★★★☆☆
結(jié)構(gòu)型模式(Structural Pattern) 組合模式(Composite Pattern) ★★★☆☆ ★★★★☆
結(jié)構(gòu)型模式(Structural Pattern) 裝飾模式(Decorator Pattern) ★★★☆☆ ★★★☆☆
結(jié)構(gòu)型模式(Structural Pattern) 外觀模式(Fa?ade Pattern) ★☆☆☆☆ ★★★★★
結(jié)構(gòu)型模式(Structural Pattern) 享元模式(Flyweight Pattern) ★★★★☆ ★☆☆☆☆
結(jié)構(gòu)型模式(Structural Pattern) 代理模式(Proxy Pattern) ★★★☆☆ ★★★★☆
行為型模式(Behavioral Pattern) 職責(zé)鏈模式(Chain of Responsibility Pattern) ★★★☆☆ ★★☆☆☆
行為型模式(Behavioral Pattern) 命令模式(Command Pattern) ★★★☆☆ ★★★★☆
行為型模式(Behavioral Pattern) 解釋器模式(Interpreter Pattern) ★★★★★ ★☆☆☆☆
行為型模式(Behavioral Pattern) 迭代器模式(Iterator Pattern) ★★★☆☆ ★★★★★
行為型模式(Behavioral Pattern) 中介者模式(Mediator Pattern) ★★★☆☆ ★★☆☆☆
行為型模式(Behavioral Pattern) 備忘錄模式(Memento Pattern) ★★☆☆☆ ★★☆☆☆
行為型模式(Behavioral Pattern) 觀察者模式(Observer Pattern) ★★★☆☆ ★★★★★
行為型模式(Behavioral Pattern) 狀態(tài)模式(State Pattern) ★★★☆☆ ★★★☆☆
行為型模式(Behavioral Pattern) 策略模式(Strategy Pattern) ★☆☆☆☆ ★★★★☆
行為型模式(Behavioral Pattern) 模板方法模式(Template Method Pattern) ★★☆☆☆ ★★★☆☆
行為型模式(Behavioral Pattern) 訪問者模式(Visitor Pattern) ★★★★☆ ★☆☆☆☆

設(shè)計(jì)模式詳解

適配器模式

適配器模式中引入了一個(gè)被稱為適配器(Adapter)的包裝類,而它所包裝的對(duì)象稱為適配者(Adaptee),即被適配的類。適配器的實(shí)現(xiàn)就是把客戶類的請(qǐng)求轉(zhuǎn)化為對(duì)適配者的相應(yīng)接口的調(diào)用。也就是說:當(dāng)客戶類調(diào)用適配器的方法時(shí),在適配器類的內(nèi)部將調(diào)用適配者類的方法,而這個(gè)過程對(duì)客戶類是透明的,客戶類并不直接訪問適配者類。因此,適配器讓那些由于接口不兼容而不能交互的類可以一起工作。
適配器模式(Adapter Pattern):將一個(gè)接口轉(zhuǎn)換成客戶希望的另一個(gè)接口,使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。適配器模式既可以作為類結(jié)構(gòu)型模式,也可以作為對(duì)象結(jié)構(gòu)型模式。
根據(jù)適配器類與適配者類的關(guān)系不同,適配器模式可分為對(duì)象適配器和類適配器兩種,在對(duì)象適配器模式中,適配器與適配者之間是關(guān)聯(lián)關(guān)系;在類適配器模式中,適配器與適配者之間是繼承(或?qū)崿F(xiàn))關(guān)系。在實(shí)際開發(fā)中,對(duì)象適配器的使用頻率更高,對(duì)象適配器模式結(jié)構(gòu)圖如下圖所示:


對(duì)象適配器模式結(jié)構(gòu)圖

在對(duì)象適配器模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
● Target(目標(biāo)抽象類):目標(biāo)抽象類定義客戶所需接口,可以是一個(gè)抽象類或接口,也可以是具體類。
● Adapter(適配器類):適配器可以調(diào)用另一個(gè)接口,作為一個(gè)轉(zhuǎn)換器,對(duì)Adaptee和Target進(jìn)行適配,適配器類是適配器模式的核心,在對(duì)象適配器中,它通過繼承Target并關(guān)聯(lián)一個(gè)Adaptee對(duì)象使二者產(chǎn)生聯(lián)系。
● Adaptee(適配者類):適配者即被適配的角色,它定義了一個(gè)已經(jīng)存在的接口,這個(gè)接口需要適配,適配者類一般是一個(gè)具體類,包含了客戶希望使用的業(yè)務(wù)方法,在某些情況下可能沒有適配者類的源代碼。

類適配器模式和對(duì)象適配器模式最大的區(qū)別在于適配器和適配者之間的關(guān)系不同,對(duì)象適配器模式中適配器和適配者之間是關(guān)聯(lián)關(guān)系,而類適配器模式中適配器和適配者是繼承關(guān)系,類適配器模式結(jié)構(gòu)如下圖所示:


類適配器模式結(jié)構(gòu)圖

由于Java、C#等語言不支持多重類繼承,因此類適配器的使用受到很多限制,例如如果目標(biāo)抽象類Target不是接口,而是一個(gè)類,就無法使用類適配器;此外,如果適配者Adaptee為最終(Final)類,也無法使用類適配器。在Java等面向?qū)ο缶幊陶Z言中,大部分情況下我們使用的是對(duì)象適配器,類適配器較少使用。

缺省適配器模式是適配器模式的一種變體,其應(yīng)用也較為廣泛。
缺省適配器模式(Default Adapter Pattern):當(dāng)不需要實(shí)現(xiàn)一個(gè)接口所提供的所有方法時(shí),可先設(shè)計(jì)一個(gè)抽象類實(shí)現(xiàn)該接口,并為接口中每個(gè)方法提供一個(gè)默認(rèn)實(shí)現(xiàn)(空方法),那么該抽象類的子類可以選擇性地覆蓋父類的某些方法來實(shí)現(xiàn)需求,它適用于不想使用一個(gè)接口中的所有方法的情況,又稱為單接口適配器模式。
● ServiceInterface(適配者接口):它是一個(gè)接口,通常在該接口中聲明了大量的方法。
● AbstractServiceClass(缺省適配器類):它是缺省適配器模式的核心類,使用空方法的形式實(shí)現(xiàn)了在ServiceInterface接口中聲明的方法。通常將它定義為抽象類,因?yàn)閷?duì)它進(jìn)行實(shí)例化沒有任何意義。
● ConcreteServiceClass(具體業(yè)務(wù)類):它是缺省適配器類的子類,在沒有引入適配器之前,它需要實(shí)現(xiàn)適配者接口,因此需要實(shí)現(xiàn)在適配者接口中定義的所有方法,而對(duì)于一些無須使用的方法也不得不提供空實(shí)現(xiàn)。在有了缺省適配器之后,可以直接繼承該適配器類,根據(jù)需要有選擇性地覆蓋在適配器類中定義的方法。

適配器模式總結(jié)
適配器模式將現(xiàn)有接口轉(zhuǎn)化為客戶類所期望的接口,實(shí)現(xiàn)了對(duì)現(xiàn)有類的復(fù)用,它是一種使用頻率非常高的設(shè)計(jì)模式,在軟件開發(fā)中得以廣泛應(yīng)用,在Spring等開源框架、驅(qū)動(dòng)程序設(shè)計(jì)(如JDBC中的數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序)中也使用了適配器模式。

  1. 主要優(yōu)點(diǎn)
    無論是對(duì)象適配器模式還是類適配器模式都具有如下優(yōu)點(diǎn):
    (1) 將目標(biāo)類和適配者類解耦,通過引入一個(gè)適配器類來重用現(xiàn)有的適配者類,無須修改原有結(jié)構(gòu)。
    (2) 增加了類的透明性和復(fù)用性,將具體的業(yè)務(wù)實(shí)現(xiàn)過程封裝在適配者類中,對(duì)于客戶端類而言是透明的,而且提高了適配者的復(fù)用性,同一個(gè)適配者類可以在多個(gè)不同的系統(tǒng)中復(fù)用。
    (3) 靈活性和擴(kuò)展性都非常好,通過使用配置文件,可以很方便地更換適配器,也可以在不修改原有代碼的基礎(chǔ)上增加新的適配器類,完全符合“開閉原則”。

具體來說,類適配器模式還有如下優(yōu)點(diǎn):
由于適配器類是適配者類的子類,因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強(qiáng)。

對(duì)象適配器模式還有如下優(yōu)點(diǎn):
(1) 一個(gè)對(duì)象適配器可以把多個(gè)不同的適配者適配到同一個(gè)目標(biāo);
(2) 可以適配一個(gè)適配者的子類,由于適配器和適配者之間是關(guān)聯(lián)關(guān)系,根據(jù)“里氏代換原則”,適配者的子類也可通過該適配器進(jìn)行適配。

  1. 主要缺點(diǎn)
    類適配器模式的缺點(diǎn)如下:
    (1) 對(duì)于Java、C#等不支持多重類繼承的語言,一次最多只能適配一個(gè)適配者類,不能同時(shí)適配多個(gè)適配者;
    (2) 適配者類不能為最終類,如在Java中不能為final類,C#中不能為sealed類;
    (3) 在Java、C#等語言中,類適配器模式中的目標(biāo)抽象類只能為接口,不能為類,其使用有一定的局限性。

對(duì)象適配器模式的缺點(diǎn)如下:
與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配者類的一個(gè)或多個(gè)方法,可以先做一個(gè)適配者類的子類,將適配者類的方法置換掉,然后再把適配者類的子類當(dāng)做真正的適配者進(jìn)行適配,實(shí)現(xiàn)過程較為復(fù)雜。

  1. 適用場(chǎng)景
    在以下情況下可以考慮使用適配器模式:
    (1) 系統(tǒng)需要使用一些現(xiàn)有的類,而這些類的接口(如方法名)不符合系統(tǒng)的需要,甚至沒有這些類的源代碼。
    (2) 想創(chuàng)建一個(gè)可以重復(fù)使用的類,用于與一些彼此之間沒有太大關(guān)聯(lián)的一些類,包括一些可能在將來引進(jìn)的類一起工作。

橋接模式-Bridge Pattern

如果軟件系統(tǒng)中某個(gè)類存在兩個(gè)獨(dú)立變化的維度,通過該模式可以將這兩個(gè)維度分離出來,使兩者可以獨(dú)立擴(kuò)展,讓系統(tǒng)更加符合“單一職責(zé)原則”。與多層繼承方案不同,它將兩個(gè)獨(dú)立變化的維度設(shè)計(jì)為兩個(gè)獨(dú)立的繼承等級(jí)結(jié)構(gòu),并且在抽象層建立一個(gè)抽象關(guān)聯(lián),該關(guān)聯(lián)關(guān)系類似一條連接兩個(gè)獨(dú)立繼承結(jié)構(gòu)的橋,故名橋接模式。
橋接模式用一種巧妙的方式處理多層繼承存在的問題,用抽象關(guān)聯(lián)取代了傳統(tǒng)的多層繼承,將類之間的靜態(tài)繼承關(guān)系轉(zhuǎn)換為動(dòng)態(tài)的對(duì)象組合關(guān)系,使得系統(tǒng)更加靈活,并易于擴(kuò)展,同時(shí)有效控制了系統(tǒng)中類的個(gè)數(shù)。橋接定義如下:
橋接模式(Bridge Pattern):將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地變化。它是一種對(duì)象結(jié)構(gòu)型模式,又稱為柄體(Handle and Body)模式或接口(Interface)模式。
橋接模式的結(jié)構(gòu)與其名稱一樣,存在一條連接兩個(gè)繼承等級(jí)結(jié)構(gòu)的橋,橋接模式結(jié)構(gòu)如下圖所示:


橋接模式結(jié)構(gòu)圖

在橋接模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
●Abstraction(抽象類):用于定義抽象類的接口,它一般是抽象類而不是接口,其中定義了一個(gè)Implementor(實(shí)現(xiàn)類接口)類型的對(duì)象并可以維護(hù)該對(duì)象,它與Implementor之間具有關(guān)聯(lián)關(guān)系,它既可以包含抽象業(yè)務(wù)方法,也可以包含具體業(yè)務(wù)方法。
●RefinedAbstraction(擴(kuò)充抽象類):擴(kuò)充由Abstraction定義的接口,通常情況下它不再是抽象類而是具體類,它實(shí)現(xiàn)了在Abstraction中聲明的抽象業(yè)務(wù)方法,在RefinedAbstraction中可以調(diào)用在Implementor中定義的業(yè)務(wù)方法。
●Implementor(實(shí)現(xiàn)類接口):定義實(shí)現(xiàn)類的接口,這個(gè)接口不一定要與Abstraction的接口完全一致,事實(shí)上這兩個(gè)接口可以完全不同,一般而言,Implementor接口僅提供基本操作,而Abstraction定義的接口可能會(huì)做更多更復(fù)雜的操作。Implementor接口對(duì)這些基本操作進(jìn)行了聲明,而具體實(shí)現(xiàn)交給其子類。通過關(guān)聯(lián)關(guān)系,在Abstraction中不僅擁有自己的方法,還可以調(diào)用到Implementor中定義的方法,使用關(guān)聯(lián)關(guān)系來替代繼承關(guān)系。
●ConcreteImplementor(具體實(shí)現(xiàn)類):具體實(shí)現(xiàn)Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同實(shí)現(xiàn),在程序運(yùn)行時(shí),ConcreteImplementor對(duì)象將替換其父類對(duì)象,提供給抽象類具體的業(yè)務(wù)操作方法。
在使用橋接模式時(shí),我們首先應(yīng)該識(shí)別出一個(gè)類所具有的兩個(gè)獨(dú)立變化的維度,將它們?cè)O(shè)計(jì)為兩個(gè)獨(dú)立的繼承等級(jí)結(jié)構(gòu),為兩個(gè)維度都提供抽象層,并建立抽象耦合。通常情況下,我們將具有兩個(gè)獨(dú)立變化維度的類的一些普通業(yè)務(wù)方法和與之關(guān)系最密切的維度設(shè)計(jì)為“抽象類”層次結(jié)構(gòu)(抽象部分),而將另一個(gè)維度設(shè)計(jì)為“實(shí)現(xiàn)類”層次結(jié)構(gòu)(實(shí)現(xiàn)部分)。

在軟件開發(fā)中,適配器模式通??梢耘c橋接模式聯(lián)合使用。適配器模式可以解決兩個(gè)已有接口間不兼容問題,在這種情況下被適配的類往往是一個(gè)黑盒子,有時(shí)候我們不想也不能改變這個(gè)被適配的類,也不能控制其擴(kuò)展。適配器模式通常用于現(xiàn)有系統(tǒng)與第三方產(chǎn)品功能的集成,采用增加適配器的方式將第三方類集成到系統(tǒng)中。橋接模式則不同,用戶可以通過接口繼承或類繼承的方式來對(duì)系統(tǒng)進(jìn)行擴(kuò)展。

橋接模式和適配器模式用于設(shè)計(jì)的不同階段,橋接模式用于系統(tǒng)的初步設(shè)計(jì),對(duì)于存在兩個(gè)獨(dú)立變化維度的類可以將其分為抽象化和實(shí)現(xiàn)化兩個(gè)角色,使它們可以分別進(jìn)行變化;而在初步設(shè)計(jì)完成之后,當(dāng)發(fā)現(xiàn)系統(tǒng)與已有類無法協(xié)同工作時(shí),可以采用適配器模式。但有時(shí)候在設(shè)計(jì)初期也需要考慮適配器模式,特別是那些涉及到大量第三方應(yīng)用接口的情況。

橋接模式總結(jié)
橋接模式是設(shè)計(jì)Java虛擬機(jī)和實(shí)現(xiàn)JDBC等驅(qū)動(dòng)程序的核心模式之一,應(yīng)用較為廣泛。在軟件開發(fā)中如果一個(gè)類或一個(gè)系統(tǒng)有多個(gè)變化維度時(shí),都可以嘗試使用橋接模式對(duì)其進(jìn)行設(shè)計(jì)。橋接模式為多維度變化的系統(tǒng)提供了一套完整的解決方案,并且降低了系統(tǒng)的復(fù)雜度。
1.主要優(yōu)點(diǎn)
橋接模式的主要優(yōu)點(diǎn)如下:
(1)分離抽象接口及其實(shí)現(xiàn)部分。橋接模式使用“對(duì)象間的關(guān)聯(lián)關(guān)系”解耦了抽象和實(shí)現(xiàn)之間固有的綁定關(guān)系,使得抽象和實(shí)現(xiàn)可以沿著各自的維度來變化。所謂抽象和實(shí)現(xiàn)沿著各自維度的變化,也就是說抽象和實(shí)現(xiàn)不再在同一個(gè)繼承層次結(jié)構(gòu)中,而是“子類化”它們,使它們各自都具有自己的子類,以便任何組合子類,從而獲得多維度組合對(duì)象。
(2)在很多情況下,橋接模式可以取代多層繼承方案,多層繼承方案違背了“單一職責(zé)原則”,復(fù)用性較差,且類的個(gè)數(shù)非常多,橋接模式是比多層繼承方案更好的解決方法,它極大減少了子類的個(gè)數(shù)。
(3)橋接模式提高了系統(tǒng)的可擴(kuò)展性,在兩個(gè)變化維度中任意擴(kuò)展一個(gè)維度,都不需要修改原有系統(tǒng),符合“開閉原則”。

2.主要缺點(diǎn)
橋接模式的主要缺點(diǎn)如下:
(1)橋接模式的使用會(huì)增加系統(tǒng)的理解與設(shè)計(jì)難度,由于關(guān)聯(lián)關(guān)系建立在抽象層,要求開發(fā)者一開始就針對(duì)抽象層進(jìn)行設(shè)計(jì)與編程。
(2)橋接模式要求正確識(shí)別出系統(tǒng)中兩個(gè)獨(dú)立變化的維度,因此其使用范圍具有一定的局限性,如何正確識(shí)別兩個(gè)獨(dú)立維度也需要一定的經(jīng)驗(yàn)積累。

3.適用場(chǎng)景
在以下情況下可以考慮使用橋接模式:
(1)如果一個(gè)系統(tǒng)需要在抽象化和具體化之間增加更多的靈活性,避免在兩個(gè)層次之間建立靜態(tài)的繼承關(guān)系,通過橋接模式可以使它們?cè)诔橄髮咏⒁粋€(gè)關(guān)聯(lián)關(guān)系。
(2)“抽象部分”和“實(shí)現(xiàn)部分”可以以繼承的方式獨(dú)立擴(kuò)展而互不影響,在程序運(yùn)行時(shí)可以動(dòng)態(tài)將一個(gè)抽象化子類的對(duì)象和一個(gè)實(shí)現(xiàn)化子類的對(duì)象進(jìn)行組合,即系統(tǒng)需要對(duì)抽象化角色和實(shí)現(xiàn)化角色進(jìn)行動(dòng)態(tài)耦合。
(3)一個(gè)類存在兩個(gè)(或多個(gè))獨(dú)立變化的維度,且這兩個(gè)(或多個(gè))維度都需要獨(dú)立進(jìn)行擴(kuò)展。
(4)對(duì)于那些不希望使用繼承或因?yàn)槎鄬永^承導(dǎo)致系統(tǒng)類的個(gè)數(shù)急劇增加的系統(tǒng),橋接模式尤為適用。

組合模式-Composite Pattern

對(duì)于樹形結(jié)構(gòu),當(dāng)容器對(duì)象(如文件夾)的某一個(gè)方法被調(diào)用時(shí),將遍歷整個(gè)樹形結(jié)構(gòu),尋找也包含這個(gè)方法的成員對(duì)象(可以是容器對(duì)象,也可以是葉子對(duì)象)并調(diào)用執(zhí)行,牽一而動(dòng)百,其中使用了遞歸調(diào)用的機(jī)制來對(duì)整個(gè)結(jié)構(gòu)進(jìn)行處理。由于容器對(duì)象和葉子對(duì)象在功能上的區(qū)別,在使用這些對(duì)象的代碼中必須有區(qū)別地對(duì)待容器對(duì)象和葉子對(duì)象,而實(shí)際上大多數(shù)情況下我們希望一致地處理它們,因?yàn)閷?duì)于這些對(duì)象的區(qū)別對(duì)待將會(huì)使得程序非常復(fù)雜。組合模式為解決此類問題而誕生,它可以讓葉子對(duì)象和容器對(duì)象的使用具有一致性。
組合模式(Composite Pattern):組合多個(gè)對(duì)象形成樹形結(jié)構(gòu)以表示具有“整體—部分”關(guān)系的層次結(jié)構(gòu)。組合模式對(duì)單個(gè)對(duì)象(即葉子對(duì)象)和組合對(duì)象(即容器對(duì)象)的使用具有一致性,組合模式又可以稱為“整體—部分”(Part-Whole)模式,它是一種對(duì)象結(jié)構(gòu)型模式。
在組合模式中引入了抽象構(gòu)件類Component,它是所有容器類和葉子類的公共父類,客戶端針對(duì)Component進(jìn)行編程。組合模式結(jié)構(gòu)如下圖所示:


組合模式結(jié)構(gòu)圖

在組合模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
● Component(抽象構(gòu)件):它可以是接口或抽象類,為葉子構(gòu)件和容器構(gòu)件對(duì)象聲明接口,在該角色中可以包含所有子類共有行為的聲明和實(shí)現(xiàn)。在抽象構(gòu)件中定義了訪問及管理它的子構(gòu)件的方法,如增加子構(gòu)件、刪除子構(gòu)件、獲取子構(gòu)件等。
● Leaf(葉子構(gòu)件):它在組合結(jié)構(gòu)中表示葉子節(jié)點(diǎn)對(duì)象,葉子節(jié)點(diǎn)沒有子節(jié)點(diǎn),它實(shí)現(xiàn)了在抽象構(gòu)件中定義的行為。對(duì)于那些訪問及管理子構(gòu)件的方法,可以通過異常等方式進(jìn)行處理。
● Composite(容器構(gòu)件):它在組合結(jié)構(gòu)中表示容器節(jié)點(diǎn)對(duì)象,容器節(jié)點(diǎn)包含子節(jié)點(diǎn),其子節(jié)點(diǎn)可以是葉子節(jié)點(diǎn),也可以是容器節(jié)點(diǎn),它提供一個(gè)集合用于存儲(chǔ)子節(jié)點(diǎn),實(shí)現(xiàn)了在抽象構(gòu)件中定義的行為,包括那些訪問及管理子構(gòu)件的方法,在其業(yè)務(wù)方法中可以遞歸調(diào)用其子節(jié)點(diǎn)的業(yè)務(wù)方法。

組合模式的關(guān)鍵是定義了一個(gè)抽象構(gòu)件類,它既可以代表葉子,又可以代表容器,而客戶端針對(duì)該抽象構(gòu)件類進(jìn)行編程,無須知道它到底表示的是葉子還是容器,可以對(duì)其進(jìn)行統(tǒng)一處理。同時(shí)容器對(duì)象與抽象構(gòu)件類之間還建立一個(gè)聚合關(guān)聯(lián)關(guān)系,在容器對(duì)象中既可以包含葉子,也可以包含容器,以此實(shí)現(xiàn)遞歸組合,形成一個(gè)樹形結(jié)構(gòu)。
如果不使用組合模式,客戶端代碼將過多地依賴于容器對(duì)象復(fù)雜的內(nèi)部實(shí)現(xiàn)結(jié)構(gòu),容器對(duì)象內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)的變化將引起客戶代碼的頻繁變化,帶來了代碼維護(hù)復(fù)雜、可擴(kuò)展性差等弊端。組合模式的引入將在一定程度上解決這些問題。
下面通過簡(jiǎn)單的示例代碼來分析組合模式的各個(gè)角色的用途和實(shí)現(xiàn)。對(duì)于組合模式中的抽象構(gòu)件角色,其典型代碼如下所示:

abstract class Component {
    public abstract void add(Component c); //增加成員
    public abstract void remove(Component c); //刪除成員
    public abstract Component getChild(int i); //獲取成員
    public abstract void operation();  //業(yè)務(wù)方法
}

一般將抽象構(gòu)件類設(shè)計(jì)為接口或抽象類,將所有子類共有方法的聲明和實(shí)現(xiàn)放在抽象構(gòu)件類中。對(duì)于客戶端而言,將針對(duì)抽象構(gòu)件編程,而無須關(guān)心其具體子類是容器構(gòu)件還是葉子構(gòu)件。
如果繼承抽象構(gòu)件的是葉子構(gòu)件,則其典型代碼如下所示:


class Leaf extends Component {
    public void add(Component c) { 
        //異常處理或錯(cuò)誤提示 
    }   
        
    public void remove(Component c) { 
        //異常處理或錯(cuò)誤提示 
    }
    
    public Component getChild(int i) { 
        //異常處理或錯(cuò)誤提示
        return null; 
    }
    
    public void operation() {
        //葉子構(gòu)件具體業(yè)務(wù)方法的實(shí)現(xiàn)
    } 
}

在容器構(gòu)件中實(shí)現(xiàn)了在抽象構(gòu)件中聲明的所有方法,既包括業(yè)務(wù)方法,也包括用于訪問和管理成員子構(gòu)件的方法,如add()、remove()和getChild()等方法。需要注意的是在實(shí)現(xiàn)具體業(yè)務(wù)方法時(shí),由于容器構(gòu)件充當(dāng)?shù)氖侨萜鹘巧蓡T構(gòu)件,因此它將調(diào)用其成員構(gòu)件的業(yè)務(wù)方法。在組合模式結(jié)構(gòu)中,由于容器構(gòu)件中仍然可以包含容器構(gòu)件,因此在對(duì)容器構(gòu)件進(jìn)行處理時(shí)需要使用遞歸算法,即在容器構(gòu)件的operation()方法中遞歸調(diào)用其成員構(gòu)件的operation()方法。

在使用組合模式時(shí),根據(jù)抽象構(gòu)件類的定義形式,我們可將組合模式分為透明組合模式和安全組合模式兩種形式:
(1) 透明組合模式
透明組合模式中,抽象構(gòu)件Component中聲明了所有用于管理成員對(duì)象的方法,包括add()、remove()以及getChild()等方法,這樣做的好處是確保所有的構(gòu)件類都有相同的接口。在客戶端看來,葉子對(duì)象與容器對(duì)象所提供的方法是一致的,客戶端可以相同地對(duì)待所有的對(duì)象。透明組合模式也是組合模式的標(biāo)準(zhǔn)形式,雖然上面的解決方案一在客戶端可以有不透明的實(shí)現(xiàn)方法,但是由于在抽象構(gòu)件中包含add()、remove()等方法,因此它還是透明組合模式,透明組合模式的完整結(jié)構(gòu)如下圖所示:


透明組合模式結(jié)構(gòu)圖

透明組合模式的缺點(diǎn)是不夠安全,因?yàn)槿~子對(duì)象和容器對(duì)象在本質(zhì)上是有區(qū)別的。葉子對(duì)象不可能有下一個(gè)層次的對(duì)象,即不可能包含成員對(duì)象,因此為其提供add()、remove()以及getChild()等方法是沒有意義的,這在編譯階段不會(huì)出錯(cuò),但在運(yùn)行階段如果調(diào)用這些方法可能會(huì)出錯(cuò)(如果沒有提供相應(yīng)的錯(cuò)誤處理代碼)。
(2) 安全組合模式
安全組合模式中,在抽象構(gòu)件Component中沒有聲明任何用于管理成員對(duì)象的方法,而是在Composite類中聲明并實(shí)現(xiàn)這些方法。這種做法是安全的,因?yàn)楦静幌蛉~子對(duì)象提供這些管理成員對(duì)象的方法,對(duì)于葉子對(duì)象,客戶端不可能調(diào)用到這些方法,這就是解決方案二所采用的實(shí)現(xiàn)方式。安全組合模式的結(jié)構(gòu)如下圖所示:


安全組合模式結(jié)構(gòu)圖

安全組合模式的缺點(diǎn)是不夠透明,因?yàn)槿~子構(gòu)件和容器構(gòu)件具有不同的方法,且容器構(gòu)件中那些用于管理成員對(duì)象的方法沒有在抽象構(gòu)件類中定義,因此客戶端不能完全針對(duì)抽象編程,必須有區(qū)別地對(duì)待葉子構(gòu)件和容器構(gòu)件。在實(shí)際應(yīng)用中,安全組合模式的使用頻率也非常高,在Java AWT中使用的組合模式就是安全組合模式。

組合模式總結(jié)
組合模式使用面向?qū)ο蟮乃枷雭韺?shí)現(xiàn)樹形結(jié)構(gòu)的構(gòu)建與處理,描述了如何將容器對(duì)象和葉子對(duì)象進(jìn)行遞歸組合,實(shí)現(xiàn)簡(jiǎn)單,靈活性好。由于在軟件開發(fā)中存在大量的樹形結(jié)構(gòu),因此組合模式是一種使用頻率較高的結(jié)構(gòu)型設(shè)計(jì)模式。在XML解析、組織結(jié)構(gòu)樹處理、文件系統(tǒng)設(shè)計(jì)等領(lǐng)域,組合模式都得到了廣泛應(yīng)用。

  1. 主要優(yōu)點(diǎn)
    組合模式的主要優(yōu)點(diǎn)如下:
    (1) 組合模式可以清楚地定義分層次的復(fù)雜對(duì)象,表示對(duì)象的全部或部分層次,它讓客戶端忽略了層次的差異,方便對(duì)整個(gè)層次結(jié)構(gòu)進(jìn)行控制。
    (2) 客戶端可以一致地使用一個(gè)組合結(jié)構(gòu)或其中單個(gè)對(duì)象,不必關(guān)心處理的是單個(gè)對(duì)象還是整個(gè)組合結(jié)構(gòu),簡(jiǎn)化了客戶端代碼。
    (3) 在組合模式中增加新的容器構(gòu)件和葉子構(gòu)件都很方便,無須對(duì)現(xiàn)有類庫(kù)進(jìn)行任何修改,符合“開閉原則”。
    (4) 組合模式為樹形結(jié)構(gòu)的面向?qū)ο髮?shí)現(xiàn)提供了一種靈活的解決方案,通過葉子對(duì)象和容器對(duì)象的遞歸組合,可以形成復(fù)雜的樹形結(jié)構(gòu),但對(duì)樹形結(jié)構(gòu)的控制卻非常簡(jiǎn)單。
  2. 主要缺點(diǎn)
    組合模式的主要缺點(diǎn)如下:
    在增加新構(gòu)件時(shí)很難對(duì)容器中的構(gòu)件類型進(jìn)行限制。有時(shí)候我們希望一個(gè)容器中只能有某些特定類型的對(duì)象,例如在某個(gè)文件夾中只能包含文本文件,使用組合模式時(shí),不能依賴類型系統(tǒng)來施加這些約束,因?yàn)樗鼈兌紒碜杂谙嗤某橄髮?,在這種情況下,必須通過在運(yùn)行時(shí)進(jìn)行類型檢查來實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)過程較為復(fù)雜。
  3. 適用場(chǎng)景
    在以下情況下可以考慮使用組合模式:
    (1) 在具有整體和部分的層次結(jié)構(gòu)中,希望通過一種方式忽略整體與部分的差異,客戶端可以一致地對(duì)待它們。
    (2) 在一個(gè)使用面向?qū)ο笳Z言開發(fā)的系統(tǒng)中需要處理一個(gè)樹形結(jié)構(gòu)。
    (3) 在一個(gè)系統(tǒng)中能夠分離出葉子對(duì)象和容器對(duì)象,而且它們的類型不固定,需要增加一些新的類型。

裝飾模式-Decorator Pattern

裝飾模式可以在不改變一個(gè)對(duì)象本身功能的基礎(chǔ)上給對(duì)象增加額外的新行為,在現(xiàn)實(shí)生活中,這種情況也到處存在,例如一張照片,我們可以不改變照片本身,給它增加一個(gè)相框,使得它具有防潮的功能,而且用戶可以根據(jù)需要給它增加不同類型的相框,甚至可以在一個(gè)小相框的外面再套一個(gè)大相框。
裝飾模式是一種用于替代繼承的技術(shù),它通過一種無須定義子類的方式來給對(duì)象動(dòng)態(tài)增加職責(zé),使用對(duì)象之間的關(guān)聯(lián)關(guān)系取代類之間的繼承關(guān)系。在裝飾模式中引入了裝飾類,在裝飾類中既可以調(diào)用待裝飾的原有類的方法,還可以增加新的方法,以擴(kuò)充原有類的功能。
裝飾模式(Decorator Pattern):動(dòng)態(tài)地給一個(gè)對(duì)象增加一些額外的職責(zé),就增加對(duì)象功能來說,裝飾模式比生成子類實(shí)現(xiàn)更為靈活。裝飾模式是一種對(duì)象結(jié)構(gòu)型模式。
在裝飾模式中,為了讓系統(tǒng)具有更好的靈活性和可擴(kuò)展性,我們通常會(huì)定義一個(gè)抽象裝飾類,而將具體的裝飾類作為它的子類,裝飾模式結(jié)構(gòu)如下圖所示:

裝飾模式結(jié)構(gòu)圖

在裝飾模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
● Component(抽象構(gòu)件):它是具體構(gòu)件和抽象裝飾類的共同父類,聲明了在具體構(gòu)件中實(shí)現(xiàn)的業(yè)務(wù)方法,它的引入可以使客戶端以一致的方式處理未被裝飾的對(duì)象以及裝飾之后的對(duì)象,實(shí)現(xiàn)客戶端的透明操作。
● ConcreteComponent(具體構(gòu)件):它是抽象構(gòu)件類的子類,用于定義具體的構(gòu)件對(duì)象,實(shí)現(xiàn)了在抽象構(gòu)件中聲明的方法,裝飾器可以給它增加額外的職責(zé)(方法)。
● Decorator(抽象裝飾類):它也是抽象構(gòu)件類的子類,用于給具體構(gòu)件增加職責(zé),但是具體職責(zé)在其子類中實(shí)現(xiàn)。它維護(hù)一個(gè)指向抽象構(gòu)件對(duì)象的引用,通過該引用可以調(diào)用裝飾之前構(gòu)件對(duì)象的方法,并通過其子類擴(kuò)展該方法,以達(dá)到裝飾的目的。
● ConcreteDecorator(具體裝飾類):它是抽象裝飾類的子類,負(fù)責(zé)向構(gòu)件添加新的職責(zé)。每一個(gè)具體裝飾類都定義了一些新的行為,它可以調(diào)用在抽象裝飾類中定義的方法,并可以增加新的方法用以擴(kuò)充對(duì)象的行為。
由于具體構(gòu)件類和裝飾類都實(shí)現(xiàn)了相同的抽象構(gòu)件接口,因此裝飾模式以對(duì)客戶透明的方式動(dòng)態(tài)地給一個(gè)對(duì)象附加上更多的責(zé)任,換言之,客戶端并不會(huì)覺得對(duì)象在裝飾前和裝飾后有什么不同。裝飾模式可以在不需要?jiǎng)?chuàng)造更多子類的情況下,將對(duì)象的功能加以擴(kuò)展。
透明裝飾模式與半透明裝飾模式
在實(shí)際使用過程中,由于新增行為可能需要單獨(dú)調(diào)用(調(diào)用具體裝飾器聲明的方法),因此這種形式的裝飾模式也經(jīng)常出現(xiàn),這種裝飾模式被稱為半透明(Semi-transparent)裝飾模式,而標(biāo)準(zhǔn)的裝飾模式是透明(Transparent)裝飾模式。下面我們對(duì)這兩種裝飾模式進(jìn)行較為詳細(xì)的介紹:
(1)透明裝飾模式
裝飾模式的透明性要求客戶端程序不應(yīng)該將對(duì)象聲明為具體構(gòu)件類型或具體裝飾類型,而應(yīng)該全部聲明為抽象構(gòu)件類型。對(duì)于客戶端而言,具體構(gòu)件對(duì)象和具體裝飾對(duì)象沒有任何區(qū)別。也就是抽象裝飾器中沒有單獨(dú)聲明方法。
透明裝飾模式可以讓客戶端透明地使用裝飾之前的對(duì)象和裝飾之后的對(duì)象,無須關(guān)心它們的區(qū)別,此外,還可以對(duì)一個(gè)已裝飾過的對(duì)象進(jìn)行多次裝飾,得到更為復(fù)雜、功能更為強(qiáng)大的對(duì)象。
(2)半透明裝飾模式
透明裝飾模式的設(shè)計(jì)難度較大,而且有時(shí)我們需要單獨(dú)調(diào)用新增的業(yè)務(wù)方法。為了能夠調(diào)用到新增方法,我們不得不用具體裝飾類型來定義裝飾之后的對(duì)象,而具體構(gòu)件類型還是可以使用抽象構(gòu)件類型來定義,這種裝飾模式即為半透明裝飾模式,也就是說,對(duì)于客戶端而言,具體構(gòu)件類型無須關(guān)心,是透明的;如果具體裝飾類型必須指定,那這就是不透明的。
半透明裝飾模式可以給系統(tǒng)帶來更多的靈活性,設(shè)計(jì)相對(duì)簡(jiǎn)單,使用起來也非常方便;但是其最大的缺點(diǎn)在于不能實(shí)現(xiàn)對(duì)同一個(gè)對(duì)象的多次裝飾,而且客戶端需要有區(qū)別地對(duì)待裝飾之前的對(duì)象和裝飾之后的對(duì)象。
裝飾模式注意事項(xiàng)
在使用裝飾模式時(shí),通常我們需要注意以下幾個(gè)問題:
(1) 盡量保持裝飾類的接口與被裝飾類的接口相同,這樣,對(duì)于客戶端而言,無論是裝飾之前的對(duì)象還是裝飾之后的對(duì)象都可以一致對(duì)待。這也就是說,在可能的情況下,我們應(yīng)該盡量使用透明裝飾模式。
(2) 盡量保持具體構(gòu)件類ConcreteComponent是一個(gè)“輕”類,也就是說不要把太多的行為放在具體構(gòu)件類中,我們可以通過裝飾類對(duì)其進(jìn)行擴(kuò)展。
(3) 如果只有一個(gè)具體構(gòu)件類,那么抽象裝飾類可以作為該具體構(gòu)件類的直接子類。

裝飾模式總結(jié)
裝飾模式降低了系統(tǒng)的耦合度,可以動(dòng)態(tài)增加或刪除對(duì)象的職責(zé),并使得需要裝飾的具體構(gòu)件類和具體裝飾類可以獨(dú)立變化,以便增加新的具體構(gòu)件類和具體裝飾類。在軟件開發(fā)中,裝飾模式應(yīng)用較為廣泛,例如在JavaIO中的輸入流和輸出流的設(shè)計(jì)、javax.swing包中一些圖形界面構(gòu)件功能的增強(qiáng)等地方都運(yùn)用了裝飾模式。
1.主要優(yōu)點(diǎn)
裝飾模式的主要優(yōu)點(diǎn)如下:
(1) 對(duì)于擴(kuò)展一個(gè)對(duì)象的功能,裝飾模式比繼承更加靈活性,不會(huì)導(dǎo)致類的個(gè)數(shù)急劇增加。
(2) 可以通過一種動(dòng)態(tài)的方式來擴(kuò)展一個(gè)對(duì)象的功能,通過配置文件可以在運(yùn)行時(shí)選擇不同的具體裝飾類,從而實(shí)現(xiàn)不同的行為。
(3) 可以對(duì)一個(gè)對(duì)象進(jìn)行多次裝飾,通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創(chuàng)造出很多不同行為的組合,得到功能更為強(qiáng)大的對(duì)象。
(4) 具體構(gòu)件類與具體裝飾類可以獨(dú)立變化,用戶可以根據(jù)需要增加新的具體構(gòu)件類和具體裝飾類,原有類庫(kù)代碼無須改變,符合“開閉原則”。
2.主要缺點(diǎn)
裝飾模式的主要缺點(diǎn)如下:
(1) 使用裝飾模式進(jìn)行系統(tǒng)設(shè)計(jì)時(shí)將產(chǎn)生很多小對(duì)象,這些對(duì)象的區(qū)別在于它們之間相互連接的方式有所不同,而不是它們的類或者屬性值有所不同,大量小對(duì)象的產(chǎn)生勢(shì)必會(huì)占用更多的系統(tǒng)資源,在一定程序上影響程序的性能。
(2) 裝飾模式提供了一種比繼承更加靈活機(jī)動(dòng)的解決方案,但同時(shí)也意味著比繼承更加易于出錯(cuò),排錯(cuò)也很困難,對(duì)于多次裝飾的對(duì)象,調(diào)試時(shí)尋找錯(cuò)誤可能需要逐級(jí)排查,較為繁瑣。
3.適用場(chǎng)景
在以下情況下可以考慮使用裝飾模式:
(1) 在不影響其他對(duì)象的情況下,以動(dòng)態(tài)、透明的方式給單個(gè)對(duì)象添加職責(zé)。
(2) 當(dāng)不能采用繼承的方式對(duì)系統(tǒng)進(jìn)行擴(kuò)展或者采用繼承不利于系統(tǒng)擴(kuò)展和維護(hù)時(shí)可以使用裝飾模式。不能采用繼承的情況主要有兩類:第一類是系統(tǒng)中存在大量獨(dú)立的擴(kuò)展,為支持每一種擴(kuò)展或者擴(kuò)展之間的組合將產(chǎn)生大量的子類,使得子類數(shù)目呈爆炸性增長(zhǎng);第二類是因?yàn)轭愐讯x為不能被繼承(如Java語言中的final類)。

外觀模式-Facade Pattern

在軟件開發(fā)中,有時(shí)候?yàn)榱送瓿梢豁?xiàng)較為復(fù)雜的功能,一個(gè)客戶類需要和多個(gè)業(yè)務(wù)類交互,而這些需要交互的業(yè)務(wù)類經(jīng)常會(huì)作為一個(gè)整體出現(xiàn),由于涉及到的類比較多,導(dǎo)致使用時(shí)代碼較為復(fù)雜,此時(shí),特別需要一個(gè)類似服務(wù)員一樣的角色,由它來負(fù)責(zé)和多個(gè)業(yè)務(wù)類進(jìn)行交互,而客戶類只需與該類交互。外觀模式通過引入一個(gè)新的外觀類(Facade)來實(shí)現(xiàn)該功能,外觀類充當(dāng)了軟件系統(tǒng)中的“服務(wù)員”,它為多個(gè)業(yè)務(wù)類的調(diào)用提供了一個(gè)統(tǒng)一的入口,簡(jiǎn)化了類與類之間的交互。在外觀模式中,那些需要交互的業(yè)務(wù)類被稱為子系統(tǒng)(Subsystem)。如果沒有外觀類,那么每個(gè)客戶類需要和多個(gè)子系統(tǒng)之間進(jìn)行復(fù)雜的交互,系統(tǒng)的耦合度將很大;而引入外觀類之后,客戶類只需要直接與外觀類交互,客戶類與子系統(tǒng)之間原有的復(fù)雜引用關(guān)系由外觀類來實(shí)現(xiàn),從而降低了系統(tǒng)的耦合度。
外觀模式中,一個(gè)子系統(tǒng)的外部與其內(nèi)部的通信通過一個(gè)統(tǒng)一的外觀類進(jìn)行,外觀類將客戶類與子系統(tǒng)的內(nèi)部復(fù)雜性分隔開,使得客戶類只需要與外觀角色打交道,而不需要與子系統(tǒng)內(nèi)部的很多對(duì)象打交道。
外觀模式:為子系統(tǒng)中的一組接口提供一個(gè)統(tǒng)一的入口。外觀模式定義了一個(gè)高層接口,這個(gè)接口使得這一子系統(tǒng)更加容易使用。
下圖所示的類圖可以作為描述外觀模式的結(jié)構(gòu)圖:


外觀模式結(jié)構(gòu)圖

外觀模式包含如下兩個(gè)角色:
(1) Facade(外觀角色):在客戶端可以調(diào)用它的方法,在外觀角色中可以知道相關(guān)的(一個(gè)或者多個(gè))子系統(tǒng)的功能和責(zé)任;在正常情況下,它將所有從客戶端發(fā)來的請(qǐng)求委派到相應(yīng)的子系統(tǒng)去,傳遞給相應(yīng)的子系統(tǒng)對(duì)象處理。
(2) SubSystem(子系統(tǒng)角色):在軟件系統(tǒng)中可以有一個(gè)或者多個(gè)子系統(tǒng)角色,每一個(gè)子系統(tǒng)可以不是一個(gè)單獨(dú)的類,而是一個(gè)類的集合,它實(shí)現(xiàn)子系統(tǒng)的功能;每一個(gè)子系統(tǒng)都可以被客戶端直接調(diào)用,或者被外觀角色調(diào)用,它處理由外觀類傳過來的請(qǐng)求;子系統(tǒng)并不知道外觀的存在,對(duì)于子系統(tǒng)而言,外觀角色僅僅是另外一個(gè)客戶端而已。
由于在外觀類中維持了對(duì)子系統(tǒng)對(duì)象的引用,客戶端可以通過外觀類來間接調(diào)用子系統(tǒng)對(duì)象的業(yè)務(wù)方法,而無須與子系統(tǒng)對(duì)象直接交互。引入外觀類后,客戶端代碼變得非常簡(jiǎn)單。

在標(biāo)準(zhǔn)的外觀模式結(jié)構(gòu)圖中,如果需要增加、刪除或更換與外觀類交互的子系統(tǒng)類,必須修改外觀類或客戶端的源代碼,這將違背開閉原則,因此可以通過引入抽象外觀類來對(duì)系統(tǒng)進(jìn)行改進(jìn),在一定程度上可以解決該問題。在引入抽象外觀類之后,客戶端可以針對(duì)抽象外觀類進(jìn)行編程,對(duì)于新的業(yè)務(wù)需求,不需要修改原有外觀類,而對(duì)應(yīng)增加一個(gè)新的具體外觀類,由新的具體外觀類來關(guān)聯(lián)新的子系統(tǒng)對(duì)象,同時(shí)通過修改配置文件來達(dá)到不修改任何源代碼并更換外觀類的目的。
如何在不修改客戶端代碼的前提下使用新的外觀類呢?解決方法之一是:引入一個(gè)抽象外觀類,客戶端針對(duì)抽象外觀類編程,而在運(yùn)行時(shí)再確定具體外觀類。

外觀模式總結(jié)
外觀模式是一種使用頻率非常高的設(shè)計(jì)模式,它通過引入一個(gè)外觀角色來簡(jiǎn)化客戶端與子系統(tǒng)之間的交互,為復(fù)雜的子系統(tǒng)調(diào)用提供一個(gè)統(tǒng)一的入口,使子系統(tǒng)與客戶端的耦合度降低,且客戶端調(diào)用非常方便。外觀模式并不給系統(tǒng)增加任何新功能,它僅僅是簡(jiǎn)化調(diào)用接口。在幾乎所有的軟件中都能夠找到外觀模式的應(yīng)用,如絕大多數(shù)B/S系統(tǒng)都有一個(gè)首頁或者導(dǎo)航頁面,大部分C/S系統(tǒng)都提供了菜單或者工具欄,在這里,首頁和導(dǎo)航頁面就是B/S系統(tǒng)的外觀角色,而菜單和工具欄就是C/S系統(tǒng)的外觀角色,通過它們用戶可以快速訪問子系統(tǒng),降低了系統(tǒng)的復(fù)雜程度。所有涉及到與多個(gè)業(yè)務(wù)對(duì)象交互的場(chǎng)景都可以考慮使用外觀模式進(jìn)行重構(gòu)。

  1. 優(yōu)點(diǎn)
    外觀模式的主要優(yōu)點(diǎn)如下:
    (1) 它對(duì)客戶端屏蔽了子系統(tǒng)組件,減少了客戶端所需處理的對(duì)象數(shù)目,并使得子系統(tǒng)使用起來更加容易。通過引入外觀模式,客戶端代碼將變得很簡(jiǎn)單,與之關(guān)聯(lián)的對(duì)象也很少。
    (2) 它實(shí)現(xiàn)了子系統(tǒng)與客戶端之間的松耦合關(guān)系,這使得子系統(tǒng)的變化不會(huì)影響到調(diào)用它的客戶端,只需要調(diào)整外觀類即可。
    (3) 一個(gè)子系統(tǒng)的修改對(duì)其他子系統(tǒng)沒有任何影響,而且子系統(tǒng)內(nèi)部變化也不會(huì)影響到外觀對(duì)象。
    2.缺點(diǎn)
    外觀模式的主要缺點(diǎn)如下:
    (1) 不能很好地限制客戶端直接使用子系統(tǒng)類,如果對(duì)客戶端訪問子系統(tǒng)類做太多的限制則減少了可變性和靈活性。
    (2) 如果設(shè)計(jì)不當(dāng),增加新的子系統(tǒng)可能需要修改外觀類的源代碼,違背了開閉原則。
  2. 模式適用場(chǎng)景
    在以下情況下可以考慮使用外觀模式:
    (1) 當(dāng)要為訪問一系列復(fù)雜的子系統(tǒng)提供一個(gè)簡(jiǎn)單入口時(shí)可以使用外觀模式。
    (2) 客戶端程序與多個(gè)子系統(tǒng)之間存在很大的依賴性。引入外觀類可以將子系統(tǒng)與客戶端解耦,從而提高子系統(tǒng)的獨(dú)立性和可移植性。
    (3) 在層次化結(jié)構(gòu)中,可以使用外觀模式定義系統(tǒng)中每一層的入口,層與層之間不直接產(chǎn)生聯(lián)系,而通過外觀類建立聯(lián)系,降低層之間的耦合度。

享元模式-Flyweight Pattern

當(dāng)一個(gè)軟件系統(tǒng)在運(yùn)行時(shí)產(chǎn)生的對(duì)象數(shù)量太多,將導(dǎo)致運(yùn)行代價(jià)過高,帶來系統(tǒng)性能下降等問題。例如在一個(gè)文本字符串中存在很多重復(fù)的字符,如果每一個(gè)字符都用一個(gè)單獨(dú)的對(duì)象來表示,將會(huì)占用較多的內(nèi)存空間,那么我們?nèi)绾稳ケ苊庀到y(tǒng)中出現(xiàn)大量相同或相似的對(duì)象,同時(shí)又不影響客戶端程序通過面向?qū)ο蟮姆绞綄?duì)這些對(duì)象進(jìn)行操作?享元模式正為解決這一類問題而誕生。享元模式通過共享技術(shù)實(shí)現(xiàn)相同或相似對(duì)象的重用,在邏輯上每一個(gè)出現(xiàn)的字符都有一個(gè)對(duì)象與之對(duì)應(yīng),然而在物理上它們卻共享同一個(gè)享元對(duì)象,這個(gè)對(duì)象可以出現(xiàn)在一個(gè)字符串的不同地方,相同的字符對(duì)象都指向同一個(gè)實(shí)例,在享元模式中,存儲(chǔ)這些共享實(shí)例對(duì)象的地方稱為享元池(Flyweight Pool)。我們可以針對(duì)每一個(gè)不同的字符創(chuàng)建一個(gè)享元對(duì)象,將其放在享元池中,需要時(shí)再?gòu)南碓厝〕觥?/p>

享元模式以共享的方式高效地支持大量細(xì)粒度對(duì)象的重用,享元對(duì)象能做到共享的關(guān)鍵是區(qū)分了內(nèi)部狀態(tài)(Intrinsic State)和外部狀態(tài)(Extrinsic State)。下面將對(duì)享元的內(nèi)部狀態(tài)和外部狀態(tài)進(jìn)行簡(jiǎn)單的介紹:
(1) 內(nèi)部狀態(tài)是存儲(chǔ)在享元對(duì)象內(nèi)部并且不會(huì)隨環(huán)境改變而改變的狀態(tài),內(nèi)部狀態(tài)可以共享。如字符的內(nèi)容,不會(huì)隨外部環(huán)境的變化而變化,無論在任何環(huán)境下字符“a”始終是“a”,都不會(huì)變成“b”。
(2) 外部狀態(tài)是隨環(huán)境改變而改變的、不可以共享的狀態(tài)。享元對(duì)象的外部狀態(tài)通常由客戶端保存,并在享元對(duì)象被創(chuàng)建之后,需要使用的時(shí)候再傳入到享元對(duì)象內(nèi)部。一個(gè)外部狀態(tài)與另一個(gè)外部狀態(tài)之間是相互獨(dú)立的。如字符的顏色,可以在不同的地方有不同的顏色,例如有的“a”是紅色的,有的“a”是綠色的,字符的大小也是如此,有的“a”是五號(hào)字,有的“a”是四號(hào)字。而且字符的顏色和大小是兩個(gè)獨(dú)立的外部狀態(tài),它們可以獨(dú)立變化,相互之間沒有影響,客戶端可以在使用時(shí)將外部狀態(tài)注入享元對(duì)象中。

正因?yàn)閰^(qū)分了內(nèi)部狀態(tài)和外部狀態(tài),我們可以將具有相同內(nèi)部狀態(tài)的對(duì)象存儲(chǔ)在享元池中,享元池中的對(duì)象是可以實(shí)現(xiàn)共享的,需要的時(shí)候就將對(duì)象從享元池中取出,實(shí)現(xiàn)對(duì)象的復(fù)用。通過向取出的對(duì)象注入不同的外部狀態(tài),可以得到一系列相似的對(duì)象,而這些對(duì)象在內(nèi)存中實(shí)際上只存儲(chǔ)一份。
享元模式(Flyweight Pattern):運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度對(duì)象的復(fù)用。系統(tǒng)只使用少量的對(duì)象,而這些對(duì)象都很相似,狀態(tài)變化很小,可以實(shí)現(xiàn)對(duì)象的多次復(fù)用。由于享元模式要求能夠共享的對(duì)象必須是細(xì)粒度對(duì)象,因此它又稱為輕量級(jí)模式,它是一種對(duì)象結(jié)構(gòu)型模式。

享元模式結(jié)構(gòu)較為復(fù)雜,一般結(jié)合工廠模式一起使用,在它的結(jié)構(gòu)圖中包含了一個(gè)享元工廠類,其結(jié)構(gòu)圖如下圖所示


享元模式結(jié)構(gòu)圖

在享元模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
● Flyweight(抽象享元類):通常是一個(gè)接口或抽象類,在抽象享元類中聲明了具體享元類公共的方法,這些方法可以向外界提供享元對(duì)象的內(nèi)部數(shù)據(jù)(內(nèi)部狀態(tài)),同時(shí)也可以通過這些方法來設(shè)置外部數(shù)據(jù)(外部狀態(tài))。
● ConcreteFlyweight(具體享元類):它實(shí)現(xiàn)了抽象享元類,其實(shí)例稱為享元對(duì)象;在具體享元類中為內(nèi)部狀態(tài)提供了存儲(chǔ)空間。通常我們可以結(jié)合單例模式來設(shè)計(jì)具體享元類,為每一個(gè)具體享元類提供唯一的享元對(duì)象。
● UnsharedConcreteFlyweight(非共享具體享元類):并不是所有的抽象享元類的子類都需要被共享,不能被共享的子類可設(shè)計(jì)為非共享具體享元類;當(dāng)需要一個(gè)非共享具體享元類的對(duì)象時(shí)可以直接通過實(shí)例化創(chuàng)建。
● FlyweightFactory(享元工廠類):享元工廠類用于創(chuàng)建并管理享元對(duì)象,它針對(duì)抽象享元類編程,將各種類型的具體享元對(duì)象存儲(chǔ)在一個(gè)享元池中,享元池一般設(shè)計(jì)為一個(gè)存儲(chǔ)“鍵值對(duì)”的集合(也可以是其他類型的集合),可以結(jié)合工廠模式進(jìn)行設(shè)計(jì);當(dāng)用戶請(qǐng)求一個(gè)具體享元對(duì)象時(shí),享元工廠提供一個(gè)存儲(chǔ)在享元池中已創(chuàng)建的實(shí)例或者創(chuàng)建一個(gè)新的實(shí)例(如果不存在的話),返回新創(chuàng)建的實(shí)例并將其存儲(chǔ)在享元池中。

在享元模式中引入了享元工廠類,享元工廠類的作用在于提供一個(gè)用于存儲(chǔ)享元對(duì)象的享元池,當(dāng)用戶需要對(duì)象時(shí),首先從享元池中獲取,如果享元池中不存在,則創(chuàng)建一個(gè)新的享元對(duì)象返回給用戶,并在享元池中保存該新增對(duì)象。典型的享元工廠類的代碼如下:

class FlyweightFactory {
    //定義一個(gè)HashMap用于存儲(chǔ)享元對(duì)象,實(shí)現(xiàn)享元池
       private HashMap flyweights = newHashMap();
       public Flyweight getFlyweight(String key){
              //如果對(duì)象存在,則直接從享元池獲取
              if(flyweights.containsKey(key)){
                     return(Flyweight)flyweights.get(key);
              }
              //如果對(duì)象不存在,先創(chuàng)建一個(gè)新的對(duì)象添加到享元池中,然后返回
              else {
                     Flyweight fw = newConcreteFlyweight();
                     flyweights.put(key,fw);
                     return fw;
              }
       }
}

享元類的設(shè)計(jì)是享元模式的要點(diǎn)之一,在享元類中要將內(nèi)部狀態(tài)和外部狀態(tài)分開處理,通常將內(nèi)部狀態(tài)作為享元類的成員變量,而外部狀態(tài)通過注入的方式添加到享元類中。典型的享元類代碼如下所示:

class Flyweight {
     //內(nèi)部狀態(tài)intrinsicState作為成員變量,同一個(gè)享元對(duì)象其內(nèi)部狀態(tài)是一致的
       private String intrinsicState;
       public  Flyweight(String intrinsicState) {
              this.intrinsicState=intrinsicState;
       }

        //外部狀態(tài)extrinsicState在使用時(shí)由外部設(shè)置,不保存在享元對(duì)象中,即使是同一個(gè)對(duì)象,在每一次調(diào)用時(shí)也可以傳入不同的外部狀態(tài)
       public void operation(String  extrinsicState) {
              ......
       }     
}

單純享元模式和復(fù)合享元模式
標(biāo)準(zhǔn)的享元模式結(jié)構(gòu)圖中既包含可以共享的具體享元類,也包含不可以共享的非共享具體享元類。但是在實(shí)際使用過程中,我們有時(shí)候會(huì)用到兩種特殊的享元模式:?jiǎn)渭兿碓J胶蛷?fù)合享元模式,下面將對(duì)這兩種特殊的享元模式進(jìn)行簡(jiǎn)單的介紹:
1.單純享元模式
在單純享元模式中,所有的具體享元類都是可以共享的,不存在非共享具體享元類。單純享元模式的結(jié)構(gòu)如下圖所示:

單純享元模式結(jié)構(gòu)圖

2.復(fù)合享元模式
將一些單純享元對(duì)象使用組合模式加以組合,還可以形成復(fù)合享元對(duì)象,這樣的復(fù)合享元對(duì)象本身不能共享,但是它們可以分解成單純享元對(duì)象,而后者則可以共享。復(fù)合享元模式的結(jié)構(gòu)如下圖所示:
復(fù)合享元模式結(jié)構(gòu)圖

通過復(fù)合享元模式,可以確保復(fù)合享元類CompositeConcreteFlyweight中所包含的每個(gè)單純享元類ConcreteFlyweight都具有相同的外部狀態(tài),而這些單純享元的內(nèi)部狀態(tài)往往可以不同。如果希望為多個(gè)內(nèi)部狀態(tài)不同的享元對(duì)象設(shè)置相同的外部狀態(tài),可以考慮使用復(fù)合享元模式。

享元模式總結(jié)
當(dāng)系統(tǒng)中存在大量相同或者相似的對(duì)象時(shí),享元模式是一種較好的解決方案,它通過共享技術(shù)實(shí)現(xiàn)相同或相似的細(xì)粒度對(duì)象的復(fù)用,從而節(jié)約了內(nèi)存空間,提高了系統(tǒng)性能。相比其他結(jié)構(gòu)型設(shè)計(jì)模式,享元模式的使用頻率并不算太高,但是作為一種以“節(jié)約內(nèi)存,提高性能”為出發(fā)點(diǎn)的設(shè)計(jì)模式,它在軟件開發(fā)中還是得到了一定程度的應(yīng)用。
1.主要優(yōu)點(diǎn)
享元模式的主要優(yōu)點(diǎn)如下:
(1) 可以極大減少內(nèi)存中對(duì)象的數(shù)量,使得相同或相似對(duì)象在內(nèi)存中只保存一份,從而可以節(jié)約系統(tǒng)資源,提高系統(tǒng)性能。
(2) 享元模式的外部狀態(tài)相對(duì)獨(dú)立,而且不會(huì)影響其內(nèi)部狀態(tài),從而使得享元對(duì)象可以在不同的環(huán)境中被共享。
2.主要缺點(diǎn)
享元模式的主要缺點(diǎn)如下:
(1) 享元模式使得系統(tǒng)變得復(fù)雜,需要分離出內(nèi)部狀態(tài)和外部狀態(tài),這使得程序的邏輯復(fù)雜化。
(2) 為了使對(duì)象可以共享,享元模式需要將享元對(duì)象的部分狀態(tài)外部化,而讀取外部狀態(tài)將使得運(yùn)行時(shí)間變長(zhǎng)。
3.適用場(chǎng)景
在以下情況下可以考慮使用享元模式:
(1) 一個(gè)系統(tǒng)有大量相同或者相似的對(duì)象,造成內(nèi)存的大量耗費(fèi)。
(2) 對(duì)象的大部分狀態(tài)都可以外部化,可以將這些外部狀態(tài)傳入對(duì)象中。
(3) 在使用享元模式時(shí)需要維護(hù)一個(gè)存儲(chǔ)享元對(duì)象的享元池,而這需要耗費(fèi)一定的系統(tǒng)資源,因此,應(yīng)當(dāng)在需要多次重復(fù)使用享元對(duì)象時(shí)才值得使用享元模式。

代理模式-Proxy Pattern

代理模式:給某一個(gè)對(duì)象提供一個(gè)代理或占位符,并由代理對(duì)象來控制對(duì)原對(duì)象的訪問。在代理模式中引入了一個(gè)新的代理對(duì)象,代理對(duì)象在客戶端對(duì)象和目標(biāo)對(duì)象之間起到中介的作用,它去掉客戶不能看到的內(nèi)容和服務(wù)或者增添客戶需要的額外的新服務(wù)。為了保證客戶端使用的透明性,所訪問的真實(shí)對(duì)象與代理對(duì)象需要實(shí)現(xiàn)相同的接口。
代理模式的結(jié)構(gòu)比較簡(jiǎn)單,其核心是代理類,為了讓客戶端能夠一致性地對(duì)待真實(shí)對(duì)象和代理對(duì)象,在代理模式中引入了抽象層,代理模式結(jié)構(gòu)如下圖所示:


代理模式結(jié)構(gòu)圖

代理模式包含如下三個(gè)角色:
(1) Subject(抽象主題角色):它聲明了真實(shí)主題和代理主題的共同接口,這樣一來在任何使用真實(shí)主題的地方都可以使用代理主題,客戶端通常需要針對(duì)抽象主題角色進(jìn)行編程。
(2) Proxy(代理主題角色):它包含了對(duì)真實(shí)主題的引用,從而可以在任何時(shí)候操作真實(shí)主題對(duì)象;在代理主題角色中提供一個(gè)與真實(shí)主題角色相同的接口,以便在任何時(shí)候都可以替代真實(shí)主題;代理主題角色還可以控制對(duì)真實(shí)主題的使用,負(fù)責(zé)在需要的時(shí)候創(chuàng)建和刪除真實(shí)主題對(duì)象,并對(duì)真實(shí)主題對(duì)象的使用加以約束。通常,在代理主題角色中,客戶端在調(diào)用所引用的真實(shí)主題操作之前或之后還需要執(zhí)行其他操作,而不僅僅是單純調(diào)用真實(shí)主題對(duì)象中的操作。
(3) RealSubject(真實(shí)主題角色):它定義了代理角色所代表的真實(shí)對(duì)象,在真實(shí)主題角色中實(shí)現(xiàn)了真實(shí)的業(yè)務(wù)操作,客戶端可以通過代理主題角色間接調(diào)用真實(shí)主題角色中定義的操作。

在實(shí)際開發(fā)過程中,代理類的實(shí)現(xiàn)比上述代碼要復(fù)雜很多,代理模式根據(jù)其目的和實(shí)現(xiàn)方式不同可分為很多種類,其中常用的幾種代理模式簡(jiǎn)要說明如下:
(1) 遠(yuǎn)程代理(Remote Proxy):為一個(gè)位于不同的地址空間的對(duì)象提供一個(gè)本地的代理對(duì)象,這個(gè)不同的地址空間可以是在同一臺(tái)主機(jī)中,也可是在另一臺(tái)主機(jī)中,遠(yuǎn)程代理又稱為大使(Ambassador)。
(2) 虛擬代理(Virtual Proxy):如果需要?jiǎng)?chuàng)建一個(gè)資源消耗較大的對(duì)象,先創(chuàng)建一個(gè)消耗相對(duì)較小的對(duì)象來表示,真實(shí)對(duì)象只在需要時(shí)才會(huì)被真正創(chuàng)建。
(3) 保護(hù)代理(Protect Proxy):控制對(duì)一個(gè)對(duì)象的訪問,可以給不同的用戶提供不同級(jí)別的使用權(quán)限。
(4) 緩沖代理(Cache Proxy):為某一個(gè)目標(biāo)操作的結(jié)果提供臨時(shí)的存儲(chǔ)空間,以便多個(gè)客戶端可以共享這些結(jié)果。
(5) 智能引用代理(Smart Reference Proxy):當(dāng)一個(gè)對(duì)象被引用時(shí),提供一些額外的操作,例如將對(duì)象被調(diào)用的次數(shù)記錄下來等。
在這些常用的代理模式中,有些代理類的設(shè)計(jì)非常復(fù)雜,例如遠(yuǎn)程代理類,它封裝了底層網(wǎng)絡(luò)通信和對(duì)遠(yuǎn)程對(duì)象的調(diào)用,其實(shí)現(xiàn)較為復(fù)雜。

遠(yuǎn)程代理
遠(yuǎn)程代理(Remote Proxy)是一種常用的代理模式,它使得客戶端程序可以訪問在遠(yuǎn)程主機(jī)上的對(duì)象,遠(yuǎn)程主機(jī)可能具有更好的計(jì)算性能與處理速度,可以快速響應(yīng)并處理客戶端的請(qǐng)求。遠(yuǎn)程代理可以將網(wǎng)絡(luò)的細(xì)節(jié)隱藏起來,使得客戶端不必考慮網(wǎng)絡(luò)的存在??蛻舳送耆梢哉J(rèn)為被代理的遠(yuǎn)程業(yè)務(wù)對(duì)象是在本地而不是在遠(yuǎn)程,而遠(yuǎn)程代理對(duì)象承擔(dān)了大部分的網(wǎng)絡(luò)通信工作,并負(fù)責(zé)對(duì)遠(yuǎn)程業(yè)務(wù)方法的調(diào)用。
客戶端對(duì)象不能直接訪問遠(yuǎn)程主機(jī)中的業(yè)務(wù)對(duì)象,只能采取間接訪問的方式。遠(yuǎn)程業(yè)務(wù)對(duì)象在本地主機(jī)中有一個(gè)代理對(duì)象,該代理對(duì)象負(fù)責(zé)對(duì)遠(yuǎn)程業(yè)務(wù)對(duì)象的訪問和網(wǎng)絡(luò)通信,它對(duì)于客戶端對(duì)象而言是透明的??蛻舳藷o須關(guān)心實(shí)現(xiàn)具體業(yè)務(wù)的是誰,只需要按照服務(wù)接口所定義的方式直接與本地主機(jī)中的代理對(duì)象交互即可。RPC調(diào)用就是典型的遠(yuǎn)程代理。

虛擬代理
虛擬代理(Virtual Proxy)也是一種常用的代理模式,對(duì)于一些占用系統(tǒng)資源較多或者加載時(shí)間較長(zhǎng)的對(duì)象,可以給這些對(duì)象提供一個(gè)虛擬代理。在真實(shí)對(duì)象創(chuàng)建成功之前虛擬代理扮演真實(shí)對(duì)象的替身,而當(dāng)真實(shí)對(duì)象創(chuàng)建之后,虛擬代理將用戶的請(qǐng)求轉(zhuǎn)發(fā)給真實(shí)對(duì)象。
通常,在以下兩種情況下可以考慮使用虛擬代理:
(1) 由于對(duì)象本身的復(fù)雜性或者網(wǎng)絡(luò)等原因?qū)е乱粋€(gè)對(duì)象需要較長(zhǎng)的加載時(shí)間,此時(shí)可以用一個(gè)加載時(shí)間相對(duì)較短的代理對(duì)象來代表真實(shí)對(duì)象。通常在實(shí)現(xiàn)時(shí)可以結(jié)合多線程技術(shù),一個(gè)線程用于顯示代理對(duì)象,其他線程用于加載真實(shí)對(duì)象。這種虛擬代理模式可以應(yīng)用在程序啟動(dòng)的時(shí)候,由于創(chuàng)建代理對(duì)象在時(shí)間和處理復(fù)雜度上要少于創(chuàng)建真實(shí)對(duì)象,因此,在程序啟動(dòng)時(shí),可以用代理對(duì)象代替真實(shí)對(duì)象初始化,大大加速了系統(tǒng)的啟動(dòng)時(shí)間。當(dāng)需要使用真實(shí)對(duì)象時(shí),再通過代理對(duì)象來引用,而此時(shí)真實(shí)對(duì)象可能已經(jīng)成功加載完畢,可以縮短用戶的等待時(shí)間。
(2) 當(dāng)一個(gè)對(duì)象的加載十分耗費(fèi)系統(tǒng)資源的時(shí)候,也非常適合使用虛擬代理。虛擬代理可以讓那些占用大量?jī)?nèi)存或處理起來非常復(fù)雜的對(duì)象推遲到使用它們的時(shí)候才創(chuàng)建,而在此之前用一個(gè)相對(duì)來說占用資源較少的代理對(duì)象來代表真實(shí)對(duì)象,再通過代理對(duì)象來引用真實(shí)對(duì)象。為了節(jié)省內(nèi)存,在第一次引用真實(shí)對(duì)象時(shí)再創(chuàng)建對(duì)象,并且該對(duì)象可被多次重用,在以后每次訪問時(shí)需要檢測(cè)所需對(duì)象是否已經(jīng)被創(chuàng)建,因此在訪問該對(duì)象時(shí)需要進(jìn)行存在性檢測(cè),這需要消耗一定的系統(tǒng)時(shí)間,但是可以節(jié)省內(nèi)存空間,這是一種用時(shí)間換取空間的做法。
無論是以上哪種情況,虛擬代理都是用一個(gè)“虛假”的代理對(duì)象來代表真實(shí)對(duì)象,通過代理對(duì)象來間接引用真實(shí)對(duì)象,可以在一定程度上提高系統(tǒng)的性能。

代理模式總結(jié)
代理模式和裝飾模式非常類似,甚至代碼都類似。二者最主要的區(qū)別是:代理模式中,代理類對(duì)被代理的對(duì)象有控制權(quán),決定其執(zhí)行或者不執(zhí)行。而裝飾模式中,裝飾類對(duì)代理對(duì)象沒有控制權(quán),只能為其增加一層裝飾,以加強(qiáng)被裝飾對(duì)象的功能。裝飾器模式關(guān)注于在一個(gè)對(duì)象上動(dòng)態(tài)的添加方法,然而代理模式關(guān)注于控制對(duì)對(duì)象的訪問。當(dāng)使用代理模式的時(shí)候,我們常常在一個(gè)代理類中創(chuàng)建一個(gè)對(duì)象的實(shí)例。而當(dāng)我們使用裝飾器模 式的時(shí)候,我們通常的做法是將原始對(duì)象作為一個(gè)參數(shù)傳給裝飾者的構(gòu)造器。
代理模式使用到極致開發(fā)就是AOP, 這是各位采用Spring架構(gòu)開發(fā)必然要使用到的技術(shù),它就是使用了代理和反射的技術(shù)。代理模式在Java的開發(fā)中俯拾皆是, 是大家非常熟悉的模式, 應(yīng)用非常廣泛, 而裝飾模式是一個(gè)比較拘謹(jǐn)?shù)哪J剑?在實(shí)際應(yīng)用中接觸比較少, 但是也有不少框架項(xiàng)目使用了裝飾模式, 例如在JDK的java.io.*包中就大量使用裝飾模式。

  1. 模式優(yōu)點(diǎn)
    (1) 能夠協(xié)調(diào)調(diào)用者和被調(diào)用者,在一定程度上降低了系統(tǒng)的耦合度。
    (2) 客戶端可以針對(duì)抽象主題角色進(jìn)行編程,增加和更換代理類無須修改源代碼,符合開閉原則,系統(tǒng)具有較好的靈活性和可擴(kuò)展性。
    此外,不同類型的代理模式也具有獨(dú)特的優(yōu)點(diǎn),例如:
    (1) 遠(yuǎn)程代理為位于兩個(gè)不同地址空間對(duì)象的訪問提供了一種實(shí)現(xiàn)機(jī)制,可以將一些消耗資源較多的對(duì)象和操作移至性能更好的計(jì)算機(jī)上,提高系統(tǒng)的整體運(yùn)行效率。
    (2) 虛擬代理通過一個(gè)消耗資源較少的對(duì)象來代表一個(gè)消耗資源較多的對(duì)象,可以在一定程度上節(jié)省系統(tǒng)的運(yùn)行開銷。
    (3) 緩沖代理為某一個(gè)操作的結(jié)果提供臨時(shí)的緩存存儲(chǔ)空間,以便在后續(xù)使用中能夠共享這些結(jié)果,優(yōu)化系統(tǒng)性能,縮短執(zhí)行時(shí)間。
    (4) 保護(hù)代理可以控制對(duì)一個(gè)對(duì)象的訪問權(quán)限,為不同用戶提供不同級(jí)別的使用權(quán)限。
  2. 模式缺點(diǎn)
    代理模式的主要缺點(diǎn)如下:
    (1) 由于在客戶端和真實(shí)主題之間增加了代理對(duì)象,因此有些類型的代理模式可能會(huì)造成請(qǐng)求的處理速度變慢,例如保護(hù)代理。
    (2) 實(shí)現(xiàn)代理模式需要額外的工作,而且有些代理模式的實(shí)現(xiàn)過程較為復(fù)雜,例如遠(yuǎn)程代理。
  3. 模式適用場(chǎng)景
    代理模式的類型較多,不同類型的代理模式有不同的優(yōu)缺點(diǎn),它們應(yīng)用于不同的場(chǎng)合:
    (1) 當(dāng)客戶端對(duì)象需要訪問遠(yuǎn)程主機(jī)中的對(duì)象時(shí)可以使用遠(yuǎn)程代理。
    (2) 當(dāng)需要用一個(gè)消耗資源較少的對(duì)象來代表一個(gè)消耗資源較多的對(duì)象,從而降低系統(tǒng)開銷、縮短運(yùn)行時(shí)間時(shí)可以使用虛擬代理,例如一個(gè)對(duì)象需要很長(zhǎng)時(shí)間才能完成加載時(shí)。
    (3) 當(dāng)需要為某一個(gè)被頻繁訪問的操作結(jié)果提供一個(gè)臨時(shí)存儲(chǔ)空間,以供多個(gè)客戶端共享訪問這些結(jié)果時(shí)可以使用緩沖代理。通過使用緩沖代理,系統(tǒng)無須在客戶端每一次訪問時(shí)都重新執(zhí)行操作,只需直接從臨時(shí)緩沖區(qū)獲取操作結(jié)果即可。
    (4) 當(dāng)需要控制對(duì)一個(gè)對(duì)象的訪問,為不同用戶提供不同級(jí)別的訪問權(quán)限時(shí)可以使用保護(hù)代理。
    (5) 當(dāng)需要為一個(gè)對(duì)象的訪問(引用)提供一些額外的操作時(shí)可以使用智能引用代理。

參考

http://www.itdecent.cn/p/73898ff0fcde

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容