設計模式--外觀模式Facade(結構型)

benbenxiongyuan發表於2014-04-11

轉自:http://blog.csdn.net/hguisu/article/details/7533759

 

1. 概述

     外觀模式,我們通過外觀的包裝,使應用程式只能看到外觀物件,而不會看到具體的細節物件,這樣無疑會降低應用程式的複雜度,並且提高了程式的可維護性。
例子1:一個電源總開關可以控制四盞燈、一個風扇、一臺空調和一臺電視機的啟動和關閉。該電源總開關可以同時控制上述所有電器裝置,電源總開關即為該系統的外觀模式設計。

2. 問題

為了降低複雜性,常常將系統劃分為若干個子系統。但是如何做到各個系統之間的通訊和相互依賴關係達到最小呢?

3. 解決方案

外觀模式:為子系統中的一組介面提供一個一致的介面, Facade模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。引入外觀角色之後,使用者只需要直接與外觀角色互動,使用者與子系統之間的複雜關係由外觀角色來實現,從而降低了系統的耦合度。


4. 適用性

在遇到以下情況使用facade模式
    1) 當你要為一個複雜子系統提供一個簡單介面時。子系統往往因為不斷演化而變得越來越複雜。大多數模式使用時都會產生更多更小的類。
    這使得子系統更具可重用性,也更容易對子系統進行定製,但這也給那些不需要定製子系統的使用者帶來一些使用上的困難。facade可以提供一個簡單的預設檢視,
    這一檢視對大多數使用者來說已經足夠,而那些需要更多的可定製性的使用者可以越過facade層。
    2) 客戶程式與抽象類的實現部分之間存在著很大的依賴性。引入 facade將這個子系統與客戶以及其他的子系統分離,可以提高子系統的獨立性 和可移植性。
    3) 當你需要構建一個層次結構的子系統時,使用 facade模式定義子系統中每層的入口點。如果子系統之間是相互依賴的,你可以讓它們僅通過facade進行通訊,從而簡化了它們之間的依賴關係。

5. 結構


6.構建模式的組成

外觀角色(Facade):是模式的核心,他被客戶client角色呼叫,知道各個子系統的功能。同時根據客戶角色已有的需求預訂了幾種功能組合\
子系統角色(Subsystem classes):實現子系統的功能,並處理由Facade物件指派的任務。對子系統而言,facade和client角色是未知的,沒有Facade的任何相關資訊;即沒有指向Facade的例項。
客戶角色(client):呼叫facade角色獲得完成相應的功能。

7. 效果

Facade模式有下面一些優點:

1)對客戶遮蔽子系統元件減少了客戶處理的物件數目並使得子系統使用起來更加容易。通過引入外觀模式,客戶程式碼將變得很簡單,與之關聯的物件也很少。
2)實現了子系統與客戶之間的鬆耦合關係,這使得子系統的元件變化不會影響到呼叫它的客戶類,只需要調整外觀類即可。
3)降低了大型軟體系統中的編譯依賴性,並簡化了系統在不同平臺之間的移植過程,因為編譯一個子系統一般不需要編譯所有其他的子系統。一個子系統的修改對其他子系統沒有任何影響,而且子系統內部變化也不會影響到外觀物件。
4)只是提供了一個訪問子系統的統一入口,並不影響使用者直接使用子系統類
Facade模式的缺點
1) 不能很好地限制客戶使用子系統類,如果對客戶訪問子系統類做太多的限制則減少了可變性和靈活性。
2) 在不引入抽象外觀類的情況下,增加新的子系統可能需要修改外觀類或客戶端的原始碼,違背了“開閉原則”

8. 實現

我們使用開關的例子;

  1. <?php   
  2. /** 
  3.  * 外觀模式 
  4.  * 
  5.  */  
  6.  class SwitchFacade  
  7. {  
  8.     private $_light     = null;     //電燈   
  9.     private $_ac        = null;     //空調   
  10.     private $_fan       = null;     //電扇   
  11.     private $_tv        = null;     //電視   
  12.       
  13.     public function __construct()  
  14.     {  
  15.         $this->_light = new Light();  
  16.         $this->_fan = new Fan();  
  17.         $this->_ac = new AirConditioner();  
  18.         $this->_tv = new Television();  
  19.     }  
  20.     /** 
  21.      * 晚上開電燈 
  22.      * 
  23.      */  
  24.     public function method1($isOpen =1) {  
  25.         if ($isOpen == 1) {  
  26.             $this->_light->on();  
  27.             $this->_fan->on();  
  28.             $this->_ac->on();  
  29.             $this->_tv->on();  
  30.         }else{  
  31.             $this->_light->off();  
  32.             $this->_fan->off();  
  33.             $this->_ac->off();  
  34.             $this->_tv->off();  
  35.         }  
  36.   
  37.     }  
  38.     /** 
  39.      * 白天不需要電燈 
  40.      * 
  41.      */  
  42.     public function method2() {  
  43.         if ($isOpen == 1) {  
  44.             $this->_fan->on();  
  45.             $this->_ac->on();  
  46.             $this->_tv->on();  
  47.         }else{  
  48.             $this->_fan->off();  
  49.             $this->_ac->off();  
  50.             $this->_tv->off();  
  51.         }  
  52.     }  
  53. }  
  54.   
  55. /******************************************子系統類 ************/  
  56. /** 
  57.  *  
  58. */   
  59. class Light  
  60. {     
  61.     private $_isOpen = 0;  
  62.     public function on() {  
  63.         echo 'Light is open''<br/>';  
  64.         $this->_isOpen = 1;   
  65.     }  
  66.     public function off() {  
  67.         echo 'Light is off''<br/>';  
  68.         $this->_isOpen = 0;  
  69.     }  
  70. }  
  71.   
  72. class Fan  
  73. {  
  74.     private $_isOpen = 0;  
  75.     public function on() {  
  76.         echo 'Fan is open''<br/>';  
  77.         $this->_isOpen = 1;   
  78.     }  
  79.     public function off() {  
  80.         echo 'Fan is off''<br/>';  
  81.         $this->_isOpen = 0;  
  82.     }  
  83. }  
  84.   
  85. class AirConditioner  
  86. {  
  87.     private $_isOpen = 0;  
  88.     public function on() {  
  89.         echo 'AirConditioner is open''<br/>';  
  90.         $this->_isOpen = 1;   
  91.     }  
  92.     public function off() {  
  93.         echo 'AirConditioner is off''<br/>';  
  94.         $this->_isOpen = 0;  
  95.     }  
  96. }  
  97. class Television  
  98. {  
  99.     private $_isOpen = 0;  
  100.     public function on() {  
  101.         echo 'Television is open''<br/>';  
  102.         $this->_isOpen = 1;   
  103.     }  
  104.     public function off() {  
  105.         echo 'Television is off''<br/>';  
  106.         $this->_isOpen = 0;  
  107.     }  
  108. }  
  109. /** 
  110.  * 客戶類 
  111.  * 
  112.  */  
  113. class client {  
  114.     static function open() {  
  115.         $f = new  SwitchFacade();  
  116.         $f->method1(1);  
  117.     }  
  118.   
  119.     static function close() {  
  120.         $f = new  SwitchFacade();  
  121.         $f->method1(0);  
  122.     }  
  123. }  
  124. client::open();  
<?php 
/**
 * 外觀模式
 *
 */
 class SwitchFacade
{
	private $_light 	= null;	 	//電燈
	private $_ac	 	= null;		//空調
	private $_fan	 	= null;		//電扇
	private $_tv	 	= null;		//電視
	
	public function __construct()
	{
		$this->_light = new Light();
		$this->_fan = new Fan();
		$this->_ac = new AirConditioner();
		$this->_tv = new Television();
	}
	/**
	 * 晚上開電燈
	 *
	 */
	public function method1($isOpen =1) {
		if ($isOpen == 1) {
			$this->_light->on();
			$this->_fan->on();
			$this->_ac->on();
			$this->_tv->on();
		}else{
			$this->_light->off();
			$this->_fan->off();
			$this->_ac->off();
			$this->_tv->off();
		}

	}
	/**
	 * 白天不需要電燈
	 *
	 */
	public function method2() {
		if ($isOpen == 1) {
			$this->_fan->on();
			$this->_ac->on();
			$this->_tv->on();
		}else{
			$this->_fan->off();
			$this->_ac->off();
			$this->_tv->off();
		}
	}
}

/******************************************子系統類 ************/
/**
 * 
*/ 
class Light
{	
	private $_isOpen = 0;
	public function on() {
		echo 'Light is open', '<br/>';
		$this->_isOpen = 1; 
	}
	public function off() {
		echo 'Light is off', '<br/>';
		$this->_isOpen = 0;
	}
}

class Fan
{
	private $_isOpen = 0;
	public function on() {
		echo 'Fan is open', '<br/>';
		$this->_isOpen = 1; 
	}
	public function off() {
		echo 'Fan is off', '<br/>';
		$this->_isOpen = 0;
	}
}

class AirConditioner
{
	private $_isOpen = 0;
	public function on() {
		echo 'AirConditioner is open', '<br/>';
		$this->_isOpen = 1; 
	}
	public function off() {
		echo 'AirConditioner is off', '<br/>';
		$this->_isOpen = 0;
	}
}
class Television
{
	private $_isOpen = 0;
	public function on() {
		echo 'Television is open', '<br/>';
		$this->_isOpen = 1; 
	}
	public function off() {
		echo 'Television is off', '<br/>';
		$this->_isOpen = 0;
	}
}
/**
 * 客戶類
 *
 */
class client {
	static function open() {
		$f = new  SwitchFacade();
		$f->method1(1);
	}

	static function close() {
		$f = new  SwitchFacade();
		$f->method1(0);
	}
}
client::open();

11. 與其他相關模式

    1)抽象工廠模式:Abstract Factory式可以與Facade模式一起使用以提供一個介面,這一介面可用來以一種子系統獨立的方式建立子系統物件。 Abstract Factory也可以代替Facade模式隱藏那些與平臺相關的類。
    2)中介模式:Mediator模式與Facade模式的相似之處是,它抽象了一些已有的類的功能。然而,Mediator的目的是對同事之間的任意通訊進行抽象,通常集中不屬於任何單個物件的功能。
    Mediator的同事物件知道中介者並與它通訊,而不是直接與其他同類物件通訊。相對而言,Facade模式僅對子系統物件的介面進行抽象,從而使它們更容易使用;它並不定義新功能,子系統也不知道Facade的存在。
    通常來講,僅需要一個Facade物件,因此Facade物件通常屬於Singleton模式。
    3)Adapter模式
    介面卡模式是將一個介面通過適配來間接轉換為另一個介面。
    外觀模式的話,其主要是提供一個整潔的一致的介面給客戶端。

12. 總結

1)根據“單一職責原則”在軟體中將一個系統劃分為若干個子系統有利於降低整個系統的複雜性,一個常見的設計目標是使子系統間的通訊和相互依賴關係達到最小,而達到該目標的途徑之一就是引入一個外觀物件,它為子系統的訪問提供了一個簡單而單一的入口

2)外觀模式也是“迪米特法則”的體現,通過引入一個新的外觀類可以降低原有系統的複雜度,外觀類充當了客戶類與子系統類之間的“第三者”,同時降低客戶類與子系統類的耦合度。外觀模式就是實現程式碼重構以便達到“迪米特法則”要求的一個強有力的武器。

3)外觀模式要求一個子系統的外部與其內部的通訊通過一個統一的外觀物件進行,外觀類將客戶端與子系統的內部複雜性分隔開,使得客戶端只需要與外觀物件打交道,而不需要與子系統內部的很多物件打交道

4)外觀模式從很大程度上提高了客戶端使用的便捷性,使得客戶端無須關心子系統的工作細節,通過外觀角色即可呼叫相關功能。
5)不要試圖通過外觀類為子系統增加新行為 ,不要通過繼承一個外觀類在子系統中加入新的行為,這種做法是錯誤的。外觀模式的用意是為子系統提供一個集中化和簡化的溝通渠道,而不是向子系統加入新的行為,新的行為的增加應該通過修改原有子系統類或增加新的子系統類來實現,不能通過外觀類來實現。

13.模式擴充套件

一個系統有多個外觀類:
        在外觀模式中,通常只需要一個外觀類,並且此外觀類只有一個例項,換言之它是一個單例類在很多情況下為了節約系統資源,一般將外觀類設計為單例類。當然這並不意味著在整個系統裡只能有一個外觀類,在一個系統中可以設計多個外觀類,每個外觀類都負責和一些特定的子系統互動,向使用者提供相應的業務功能。
不要試圖通過外觀類為子系統增加新行為:
       不要通過繼承一個外觀類在子系統中加入新的行為,這種做法是錯誤的。外觀模式的用意是為子系統提供一個集中化和簡化的溝通渠道,而不是向子系統加入新的行為,新的行為的增加應該通過修改原有子系統類或增加新的子系統類來實現,不能通過外觀類來實現。
       外觀模式創造出一個外觀物件,將客戶端所涉及的屬於一個子系統的協作夥伴的數量減到最少,使得客戶端與子系統內部的物件的相互作用被外觀物件所取代。外觀類充當了客戶類與子系統類之間的“第三者”,降低了客戶類與子系統類之間的耦合度,外觀模式就是實現程式碼重構以便達到“迪米特法則”要求的一個強有力的武器。

抽象外觀類的引入:

外觀模式最大的缺點在於違背了“開閉原則”
當增加新的子系統或者移除子系統時需要修改外觀類,可以通過引入抽象外觀類在一定程度上解決該問題,客戶端針對抽象外觀類進行程式設計。對於新的業務需求,不修改原有外觀類,而對應增加一個新的具體外觀類,由新的具體外觀類來關聯新的子系統物件,同時通過修改配置檔案來達到不修改原始碼並更換外觀類的目的。 

UML:



相關文章