1. 概述 將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以在一起工作。 2. 解決的問題 即Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以在一起工作。 3. 模式中的角色 3.1 目標介面(Target):客戶所期待 ...
1. 概述
將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以在一起工作。
2. 解決的問題
即Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以在一起工作。
3. 模式中的角色
3.1 目標介面(Target):客戶所期待的介面。目標可以是具體的或抽象的類,也可以是介面。
3.2 源介面/類(Adaptee):需要適配的類或適配者類。
3.3 適配器(Adapter):通過包裝一個需要適配的對象,把源介面轉換成目標介面。
4. UML圖:
5 代碼實現:
<?php header("Content-type:text/html;Charset=utf-8"); //目標對象 interface Target{ function mothed(); } //源介面 interface Adaptee{ function mothed(); } class ConcreteAdaptee implements Adaptee{ function mothed(){ echo "源方法"; } } //適配器 class Adapter Implements Target{ private $adaptee; function __construct(Adaptee $adaptee){ $this->adaptee = $adaptee; } //override目標介面的方法執行的卻是源介面的方法從實現適配 function mothed(){ $this->adaptee->mothed(); } } //測試 $adaptee = new ConcreteAdaptee(); $adapter = new Adapter($adaptee); $adapter->mothed(); ?>
6:具體實例:
<?php // 代碼中有兩個介面,分別為德標介面和國標介面,分別命名為DBSocketInterface和GBSocketInterface,此外還有兩個實現類,分別為德國插座和中國插座,
分別為DBSocket和GBSocket。為了提供兩套介面之間的適配,我們提供了一個適配器,叫做SocketAdapter。除此之外,還有一個客戶端,比如是我們去德國旅游
時住的一家賓館,叫Hotel,在這個德國旅館中使用德國介面。 // 德標介面: interface DBSocketInterface{ /** * 這個方法的名字叫做:使用兩項圓頭的插口供電 * 本人英語就這個水平 */ function powerWithTwoRound(); } // 德國插座實現德標介面 class DBSocket implements DBSocketInterface{ public function powerWithTwoRound(){ echo "使用兩項圓頭的插孔供電"; } } // 德國旅館是一個客戶端,它裡面有德標的介面,可以使用這個德標介面給手機充電: class Hotel{ //旅館中有一個德標的插口 private $dbSocket; public function Hotel(DBSocketInterface $dbSocket) { $this->dbSocket = $dbSocket; } public function setSocket (DBSocketInterface $dbSocket){ $this->dbSocket = $dbSocket; } //旅館中有一個充電的功能 public function charge(){ //使用德標插口充電 $this->dbSocket->powerWithTwoRound(); } } // 現在寫一段代碼進行測試: class Test { public static function main() { //初始化一個德國插座對象, 用一個德標介面引用它 $dbSoket = new DBSocket(); //創建一個旅館對象 $hotel = new Hotel($dbSoket); //在旅館中給手機充電 $hotel->charge(); } } // 運行程式,列印出以下結果: 使用兩項圓頭的插孔供電 // 現在我去德國旅游,帶去的三項扁頭的手機充電器。如果沒有帶電源適配器,我是不能充電的,因為不可能為了我一個旅客而為我更改牆上的插座,
更不可能為我專門蓋一座使用中國國標插座的賓館。因為人家德國人一直這麼使用,並且用的挺好,俗話說入鄉隨俗,我就要自己想辦法來解決問題。
對應到我們的代碼中,也就是說,上面的Hotel類,DBSocket類,DBSocketInterface介面都是不可變的(由德國的客戶提供),如果我想使用這一套API,那麼只能自己寫代碼解決。 // 下麵是國標介面和中國插座的代碼。 // 國標介面: interface GBSocketInterface { /** * 這個方法的名字叫做:使用三項扁頭的插口供電 * */ function powerWithThreeFlat(); } // 中國插座實現國標介面: class GBSocket implements GBSocketInterface{ public function powerWithThreeFlat() { echo "使用三項扁頭插孔供電"; } } // 可以認為這兩個東西是我帶到德國去的,目前他們還不能使用,因為介面不一樣。那麼我必須創建一個適配器,這個適配器必須滿足以下條件: // 1 必須符合德國標準的介面,否則的話還是沒辦法插到德國插座中; 2 在調用上面實現的德標介面進行充電時,提供一種機制,將這個調用轉到對國標介面的調用 。 // 這就要求: 1 適配器必須實現原有的舊的介面 2 適配器對象中持有對新介面的引用,當調用舊介面時,將這個調用委托給實現新介面的對象來處理,也就是在適配器對象中組合一個新介面。 // 下麵給出適配器類的實現: class SocketAdapter implements DBSocketInterface{ //實現舊介面 //組合新介面 private $gbSocket; /** * 在創建適配器對象時,必須傳入一個新街口的實現類 * */ public function SocketAdapter(GBSocketInterface $gbSocket) { $this->gbSocket = $gbSocket; } /** * 將對就介面的調用適配到新介面 */ public function powerWithTwoRound() { $this->gbSocket->powerWithThreeFlat(); } } // 這個適配器類滿足了上面的兩個要求。下麵寫一段測試代碼來驗證一下適配器能不能工作,我們按步驟一步步的寫出代碼,以清楚的說明適配器是如何使用的。 // 1 我去德國旅游,帶去的充電器是國標的(可以將這裡的GBSocket看成是充電器) $gbSocket = new GBSocket(); // 2 來到德國後, 找到一家德國賓館住下 (這個賓館還是上面代碼中的賓館,使用的依然是德國標準的插口) $hotel = new Hotel(); // 3 由於沒法充電,我拿出隨身帶去的適配器,並且將我帶來的充電器插在適配器的上端插孔中。這個上端插孔是符合國標的,我的充電器完全可以插進去。 $socketAdapter = new SocketAdapter($gbSocket); // 4 再將適配器的下端插入賓館里的插座上 $hotel->setSocket($socketAdapter); // 5 可以在賓館中使用適配器進行充電了 $hotel->charge(); // 上面的五個步驟就是適配器的使用過程,下麵是完整的測試代碼。 class TestAdapter { public static function main() { $gbSocket = new GBSocket(); $hotel = new Hotel(); $socketAdapter = new SocketAdapter($gbSocket); $hotel->setSocket($socketAdapter); $hotel->charge(); } } ?>
7 優點
7.1 通過適配器,客戶端可以調用同一介面,因而對客戶端來說是透明的。這樣做更簡單、更直接、更緊湊。
7.2 復用了現存的類,解決了現存類和復用環境要求不一致的問題。
7.3 將目標類和適配者類解耦,通過引入一個適配器類重用現有的適配者類,而無需修改原有代碼。
7.4 一個對象適配器可以把多個不同的適配者類適配到同一個目標,也就是說,同一個適配器可以把適配者類和它的子類都適配到目標介面。
8 缺點
對於對象適配器來說,更換適配器的實現過程比較複雜。
9適用場景
9.1系統需要使用現有的類,而這些類的介面不符合系統的介面。
9.2想要建立一個可以重用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。
9.3 兩個類所做的事情相同或相似,但是具有不同介面的時候。
9.4 舊的系統開發的類已經實現了一些功能,但是客戶端卻只能以另外介面的形式訪問,但我們不希望手動更改原有類的時候。
9.5 使用第三方組件,組件介面定義和自己定義的不同,不希望修改自己的介面,但是要使用第三方組件介面的功能。