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

1. 概述

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

2. 問題

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

3. 解決方案

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

1.jpg

4.適用性

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

5. 結構

4.jpg

5.jpg

6.構建模式的組成

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

7.效果

Facade模式有下面一些優點:

1)對客戶屏蔽子系統組件,減少了客戶處理的對象數目并使得子系統使用起來更加容易。通過引入外觀模式,客戶代碼將變得很簡單,與之關聯的對象也很少。

2)實現了子系統與客戶之間的松耦合關系,這使得子系統的組件變化不會影響到調用它的客戶類,只需要調整外觀類即可。

3)降低了大型軟件系統中的編譯依賴性,并簡化了系統在不同平臺之間的移植過程,因為編譯一個子系統一般不需要編譯所有其他的子系統。一個子系統的修改對其他子系統沒有任何影響,而且子系統內部變化也不會影響到外觀對象。

4)只是提供了一個訪問子系統的統一入口,并不影響用戶直接使用子系統類。

Facade模式的缺點

1) 不能很好地限制客戶使用子系統類,如果對客戶訪問子系統類做太多的限制則減少了可變性和靈活性。

2) 在不引入抽象外觀類的情況下,增加新的子系統可能需要修改外觀類或客戶端的源代碼,違背了“開閉原則”。

8.實現

我們使用開關的例子;

<?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:

6.jpg

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

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/2933

(0)
s19930811s19930811
上一篇 2015-07-04
下一篇 2015-07-06

相關推薦

  • 馬哥教育網絡班20期+第5周課程練習

    1、顯示/boot/grub/grub.conf中以至少一個空白字符開頭的行; [root@localhost ~]# grep '^[[:space:]]\+' /boot/grub/grub.conf 2、顯示/etc/rc.d/rc.sysinit文件中以#開頭,后面跟至少一個空白字符,而后又有至…

    系統運維 2016-07-12
  • 第五周作業

    1、顯示/boot/grub/grub.conf中以至少一個空白字符開頭的行;grep “^[[:space:]]+”  /boot/grub/grub.conf 2、顯示/etc/rc.d/rc.sysinit文件中以#開頭,后面跟至少一個空白字符,而后又有至少一個非空白字符的行;[root@localhost ~…

    Linux干貨 2017-03-10
  • Http協議中的各種長度限制總結

    HTTP1.0的格式 request(HTTP請求消息)結構:一個請求行.部分消息頭,以及實體內容,其中的一些消息內容都是可選擇的.消息頭和實體內容之間要用空行分開. GET /index.html HTTP/1.1 //請求頭,下面都是消息頭.Accept: */*Accept-Languang:en-usConnection:keep-aliveHost…

    Linux干貨 2015-04-04
  • 基于Docker的工作流

    這次我們創建一個Hello world的web服務器。 一  mkdir -p identidock/app   #首先創建一個新的multiidentidock來存放我們的項目,在這個目錄下面,創建一個app目錄來存放Python代碼。 touch app/identidock.py&n…

    Linux干貨 2016-03-03
  • 馬哥教育網絡班22期-第13周博客作業

    第13周博客作業 1、建立samba共享,共享目錄為/data,要求:(描述完整的過程)   1)共享名為shared,工作組為zhucke;   2)添加組develop,添加用戶gentoo,centos和ubuntu,其中gentoo和centos以develop為附加組,ubuntu不屬于develop組;密碼均為用戶名; &nb…

    Linux干貨 2016-12-26
  • Btrfs文件系統

    一:概述     1.1簡介         Btrfs被稱為是下一代Linux文件系統。通常念成 Butter FS,Better FS 或B-tree FS。它采用了很多先進的文件系統設計,不僅解決了 ext2/3的擴展性問題,支持寫時…

    Linux干貨 2016-04-19
欧美性久久久久