我在看代理模式,對我而言,它彷佛很像裝飾器,適配器和橋模式。 我誤會了什麼嗎? 有什麼不一樣? 爲何我會使用Proxy模式而不是其餘模式? 你過去在現實世界的項目中如何使用它們? html
全部這四種模式都涉及用內部對象包裝內部對象/類,所以它們在結構上很是類似。 我會按目的概述差別: java
並經過內部和外部對象之間的接口變化: 設計模式
我對這個問題的見解。 api
全部四種模式都有不少共同點,全部這四種模式有時被非正式地稱爲包裝器或包裝器模式。 全部使用組合,包裝主題並在某個時刻將執行委託給主題,將一個方法調用映射到另外一個方法調用。 它們使客戶無需必須構建不一樣的對象並複製全部相關數據。 若是使用得當,它們能夠節省內存和處理器。 安全
經過促進鬆散耦合,他們可使穩定的代碼更少地暴露於不可避免的變化,而且對於其餘開發人 服務器
適配器 網絡
適配器使主體(適配器)適應不一樣的接口。 這樣咱們就能夠將對象添加到名義上不一樣類型的集合中。 oracle
適配器僅向客戶端公開相關方法,能夠限制全部其餘方法,揭示特定上下文的使用意圖,如調整外部庫,使其看起來不那麼通用,更專一於咱們的應用程序需求。 適配器提升了代碼的可讀性和自我描述。 框架
適配器使一個團隊免受其餘團隊的易變代碼的攻擊; 處理離岸團隊時的救生員工具;-) 函數
較少說起的目的是防止主題類過多的註釋。 有了這麼多基於註釋的框架,這就變得愈來愈重要了。
適配器有助於克服僅限單一繼承的Java限制。 它能夠將幾個適應者組合在一個信封下,給人以多重繼承的印象。
代碼方面,適配器是「瘦」。 除了簡單地調用adaptee方法和進行此類調用所必需的偶爾數據轉換以外,它不該該向adaptee類添加太多代碼。
JDK或基本庫中沒有不少好的適配器示例。 應用程序開發人員建立適配器,以使庫適應應用程序特定的接口
裝飾
Decorator不只委託,不只將一個方法映射到另外一個方法,它們作得更多,它們修改了一些主題方法的行爲,它能夠決定不調用主題方法,委託給不一樣的對象,一個輔助對象。
裝飾器一般將(透明)功能添加到包裝對象,如記錄,加密,格式化或壓縮到主題。 這個新功能可能會帶來不少新代碼。 所以,裝飾器一般比適配器「更胖」。
裝飾器必須是主題界面的子類。 它們能夠透明地使用而不是它的主題。 請參閱BufferedOutputStream,它仍然是OutputStream,能夠這樣使用。 這是Adapters的主要技術差別。
JDK中的整個裝飾器系列的教科書示例很容易 - Java IO。 BufferedOutputStream , FilterOutputStream和ObjectOutputStream等全部類都是OutputStream的裝飾器。 它們能夠是洋蔥層,再一次裝飾一個裝飾,增長更多功能。
代理
代理不是典型的包裝器。 在建立代理時,包裝對象(代理主題)可能尚不存在。 代理一般在內部建立它。 它多是按需建立的繁重對象,也多是不一樣JVM或不一樣網絡節點中的遠程對象,甚至是非Java對象(本機代碼中的組件)。 它根本沒有必要包裝或委託給另外一個對象。
最典型的例子是遠程代理,重對象初始化器和訪問代理。
遠程代理 - 主題在遠程服務器,不一樣的JVM甚至非Java系統上。 代理將方法調用轉換爲RMI / REST / SOAP調用或任何須要的內容,從而防止客戶端暴露於底層技術。
延遲加載代理 - 僅對第一次使用或第一次密集使用徹底初始化對象。
訪問代理 - 控制對主題的訪問。
正面
立面與最小知識的設計原則(德米特定律)密切相關。 Facade與Adapter很是類似。 它們都包裝,它們都將一個對象映射到另外一個對象,但它們的意圖不一樣。 Facade展平了主題的複雜結構,複雜的對象圖,簡化了對複雜結構的訪問。
Facade包裹着一個複雜的結構,爲它提供了一個平面界面。 這能夠防止客戶端對象暴露於主題結構中的內部關係,從而促進鬆耦合。
橋
適配器模式的更復雜的變體,其中不只實現變化並且抽象。 它爲表明團增長了一個間接性。 額外的受權是橋樑。 它甚至能夠從適配接口中解耦適配器。 它比任何其餘包裝模式更增長複雜性,所以請當心使用。
構造函數的差別
在查看構造函數時,模式差別也很明顯。
代理不包裝現有對象。 構造函數中沒有主題。
裝飾器和適配器確實包裝已經存在的對象,一般是這樣
在構造函數中提供。
Facade構造函數獲取整個對象圖的根元素,不然它看起來與Adapter相同。
現實生活中的例子 - JAXB編組適配器 。 此適配器的用途是將簡單的平面類映射到外部所需的更復雜的結構,並防止使用過多的註釋「污染」主題類。
設計模式不是數學,它是藝術與軟件工程的結合。 沒有什麼比這個要求你必須使用代理,橋接等。建立設計模式來解決問題。 若是您預計到設計問題,請使用它。 根據經驗,您將瞭解具體問題,使用哪一種模式。 若是你擅長堅實的設計原則,你就能夠實現設計模式而不須要知道模式。 常見的例子是statergy和工廠模式
所以,更多地關注固體設計原則,清潔編碼原則和ttd
這是Head First Design Patterns的引用
定義屬於書。 例子屬於我。
裝飾器 - 不改變界面,但增長了責任。 假設你有一個汽車界面,當你爲汽車的不一樣型號(s,sv,sl)實現這個時,你可能須要爲某些型號增長更多的責任 。 喜歡天窗,安全氣囊等。
適配器 - 將一個接口轉換爲另外一個接口。 你有一個汽車界面,你但願它像吉普車同樣。 因此你拿車,修改它,變成吉普車。 由於它不是真正的吉普車。 但就像吉普車同樣。
Facade - 使界面更簡單。 假設您有汽車,飛機,船舶接口。 實際上,你所須要的只是一個將人們從一個地方送到另外一個地方的班級。 您但願外觀決定使用哪一種車輛。 而後你收集全部這些接口引用 ,並讓它決定/委託以保持簡單。
首先:「一個外觀不只簡化了界面,它將客戶端與組件子系統分離。外觀和適配器能夠包裝多個類,但外觀的意圖是簡化,而適配器是將接口轉換爲不一樣的東西。 「
我想在Bill Karwing的回答中添加一些例子(這很棒。)我還添加了一些實現的關鍵差別,我以爲很遺憾
引用的部分來自[ https://stackoverflow.com/a/350471/1984346](Bill Karwing)的回答
代理,裝飾器,適配器和橋都是「包裝」類的變體。 但他們的用途不一樣。
- 當您想要延遲實例化對象時,可使用代理 ,或者隱藏您正在調用遠程服務或控制對象訪問的事實。
代理的ProxyClass和ObjectClass應該實現相同的接口,所以它們是可互換的
示例 - 代理昂貴的對象
class ProxyHumanGenome implements GenomeInterface { private $humanGenome = NULL; // humanGenome class is not instantiated at construct time function __construct() { } function getGenomeCount() { if (NULL == $this->humanGenome) { $this->instantiateGenomeClass(); } return $this->humanGenome->getGenomeCount(); } } class HumanGenome implement GenomeInterface { ... }
- Decorator也被稱爲「智能代理」。 當您想要向對象添加功能時使用此功能,但不能經過擴展該對象的類型來使用。 這容許您在運行時執行此操做。
DecoratorClass應該(能夠)實現ObjectClass的擴展接口。 因此ObjectClass能夠被DecoratorClass替換,但反之亦然。
示例 - 添加附加功能
class DecoratorHumanGenome implements CheckGenomeInterface { // ... same code as previous example // added functionality public function isComplete() { $this->humanGenome->getCount >= 21000 } } interface CheckGenomeInterface extends GenomeInterface { public function isComplete(); } class HumanGenome implement GenomeInterface { ... }
- 當您具備抽象接口時,將使用適配器 ,而且您但願將該接口映射到具備相似功能角色但具備不一樣接口的另外一個對象。
實現差別代理,裝飾器,適配器
適配器爲其主題提供不一樣的界面。 代理提供相同的接口。 Decorator提供加強的界面。
Bridge與Adapter很是類似,可是當您定義抽象接口和底層實現時,咱們將其稱爲Bridge。 即,您不適應某些遺留代碼或第三方代碼,您是全部代碼的設計者,但您須要可以交換不一樣的實現。
Facade是一個更高級別(讀取:更簡單)的接口,用於一個或多個類的子系統。 假設您有一個複雜的概念,須要多個對象來表示。 對這組對象進行更改會讓人感到困惑,由於您並不老是知道哪一個對象具備您須要調用的方法。 如今是編寫Facade的時候了,它能夠爲您能夠對對象集合執行的全部複雜操做提供高級方法。 示例:學校部分的域模型,使用
countStudents()
,reportAttendance()
,assignSubstituteTeacher()
等方法。
本答案中的大部分信息來自https://sourcemaking.com/design_patterns ,我建議將其做爲設計模式的優秀資源 。