【架構—設計模式】外觀模式

外觀模式是一種使用頻率很是高的結構型設計模式,它經過引入一個外觀角色來簡化客戶端與子系統之間的交互,爲複雜的子系統調用提供一個統一的入口,下降子系統與客戶端的耦合度,且客戶端調用很是方便。php

概述

在軟件開發中,有時候爲了完成一項較爲複雜的功能,一個客戶類須要和多個業務類交互,而這些須要交互的業務類常常會做爲一個總體出現,因爲涉及到的類比較多,致使使用時代碼較爲複雜。此時,特別須要一個相似服務員同樣的角色,由它來負責和多個業務類進行交互,而客戶類只需與該類交互。外觀模式經過引入一個新的外觀類(Facade)來實現該功能,外觀類充當了軟件系統中的「服務員」,它爲多個業務類的調用提供了一個統一的入口,簡化了類與類之間的交互。在外觀模式中,那些須要交互的業務類被稱爲子系統(Subsystem)。若是沒有外觀類,那麼每一個客戶類須要和多個子系統之間進行復雜的交互,系統的耦合度將很大;而引入外觀類以後,客戶類只須要直接與外觀類交互,客戶類與子系統之間原有的複雜引用關係由外觀類來實現,從而下降了系統的耦合度。編程

外觀模式中,一個子系統的外部與其內部的通訊經過一個統一的外觀類進行,外觀類將客戶類與子系統的內部複雜性分隔開,使得客戶類只須要與外觀角色打交道,而不須要與子系統內部的不少對象打交道。設計模式

外觀模式定義以下:工具

外觀模式:爲子系統中的一組接口提供一個統一的入口。外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。

外觀模式又稱爲門面模式,它是一種對象結構型模式。外觀模式是迪米特法則的一種具體實現,經過引入一個新的外觀角色能夠下降原有系統的複雜度,同時下降客戶類與子系統的耦合度。this

外觀模式沒有一個通常化的類圖描述,下面所示的類圖能夠做爲描述外觀模式的結構圖:加密

1354636733_5965.jpg

外觀模式包含以下兩個角色:spa

  1. Facade(外觀角色):在客戶端能夠調用它的方法,在外觀角色中能夠知道相關的(一個或者多個)子系統的功能和責任;在正常狀況下,它將全部從客戶端發來的請求委派到相應的子系統去,傳遞給相應的子系統對象處理。
  2. SubSystem(子系統角色):在軟件系統中能夠有一個或者多個子系統角色,每個子系統能夠不是一個單獨的類,而是一個類的集合,它實現子系統的功能;每個子系統均可以被客戶端直接調用,或者被外觀角色調用,它處理由外觀類傳過來的請求;子系統並不知道外觀的存在,對於子系統而言,外觀角色僅僅是另一個客戶端而已。

引入外觀模式以後,增長新的子系統或者移除子系統都很是方便,客戶類無須進行修改(或者極少的修改),只須要在外觀類中增長或移除對子系統的引用便可。從這一點來講,外觀模式在必定程度上並不符合開閉原則,增長新的子系統須要對原有系統進行必定的修改,雖然這個修改工做量不大。設計

外觀模式中所指的子系統是一個廣義的概念,它能夠是一個類、一個功能模塊、系統的一個組成部分或者一個完整的系統。子系統類一般是一些業務類,實現了一些具體的、獨立的業務功能,其典型代碼以下:code

class SubSystemA
{
    public function MethodA()
    {
        //業務實現代碼
    }
}

class SubSystemB
{
    public function MethodB()
    {
        //業務實現代碼
    }
}

class SubSystemC
{
    public function MethodC()
    {
        //業務實現代碼
    }
}

在引入外觀類以後,與子系統業務類之間的交互統一由外觀類來完成,在外觀類中一般存在以下代碼:對象

class Facade
{
    private $obj1;
    private $obj2;
    private $obj3;

    public function __construct()
    {
        $this->obj1 = new SubSystemA();
        $this->obj2 = new SubSystemB();
        $this->obj3 = new SubSystemC();
    }

    public function Method()
    {
        $this->obj1->MethodA();
        $this->obj2->MethodB();
        $this->obj3->MethodC();
    }
}

因爲在外觀類中維持了對子系統對象的引用,客戶端能夠經過外觀類來間接調用子系統對象的業務方法,而無須與子系統對象直接交互。

案例

某軟件公司欲開發一個可應用於多個軟件的文件加密模塊,該模塊能夠對文件中的數據進行加密並將加密以後的數據存儲在一個新文件中,具體的流程包括三個部分,分別是讀取源文件、加密、保存加密以後的文件,其中,讀取文件和保存文件使用流來實現,加密操做經過求模運算實現。這三個操做相對獨立,爲了實現代碼的獨立重用,讓設計更符合單一職責原則,這三個操做的業務代碼封裝在三個不一樣的類中。

現使用外觀模式設計該文件加密模塊。

經過分析,本實例結構圖如圖所示:

1354688525_6684.jpg

EncryptFacade充當外觀類,FileReader、CipherMachine和FileWriter充當子系統類。示例代碼以下:

<?php

class FileReader
{
    public function Read(string $fileNameSrc): string
    {
        return file_get_contents($fileNameSrc);
    }
}

class CipherMachine
{
    public function Encrypt(string $plainText): string
    {
        return openssl_encrypt($plainText, 'DES-ECB', '123456');
    }
}

class FileWriter
{
    public function Write(string $encryptStr, string $fileNameDes)
    {
        file_put_contents($fileNameDes, $encryptStr);
    }
}


class EncryptFacade
{
    //維持對其餘對象的引用
    private $reader;
    private $cipher;
    private $writer;

    public function __construct()
    {
        $this->reader = new FileReader();
        $this->cipher = new CipherMachine();
        $this->writer = new FileWriter();
    }

    //調用其餘對象的業務方法
    public function FileEncrypt(string $fileNameSrc, string $fileNameDes)
    {
        $plainStr = $this->reader->Read($fileNameSrc);
        $encryptStr = $this->cipher->Encrypt($plainStr);
        $this->writer->Write($encryptStr, $fileNameDes);
    }
}

抽象外觀類

在標準的外觀模式結構圖中,若是須要增長、刪除或更換與外觀類交互的子系統類,必須修改外觀類或客戶端的源代碼,這將違背開閉原則,所以能夠經過引入抽象外觀類來對系統進行改進,在必定程度上能夠解決該問題。在引入抽象外觀類以後,客戶端能夠針對抽象外觀類進行編程,對於新的業務需求,不須要修改原有外觀類,而對應增長一個新的具體外觀類,由新的具體外觀類來關聯新的子系統對象,同時經過修改配置文件來達到不修改任何源代碼並更換外觀類的目的。

若是在應用實例「文件加密模塊」中須要更換一個加密類,再也不使用原有的加密類CipherMachine,而改成新加密類NewCipherMachine。若是不增長新的外觀類,只能經過修改原有外觀類EncryptFacade的源代碼來實現加密類的更換,將原有的對CipherMachine類型對象的引用改成對NewCipherMachine類型對象的引用,這違背了開閉原則,所以須要經過增長新的外觀類來實現對子系統對象引用的改變。

若是增長一個新的外觀類NewEncryptFacade來與FileReader類、FileWriter類以及新增長的NewCipherMachine類進行交互,雖然原有系統類庫無須作任何修改,可是由於客戶端代碼中原來針對EncryptFacade類進行編程,如今須要改成NewEncryptFacade類,所以須要修改客戶端源代碼。

如何在不修改客戶端代碼的前提下使用新的外觀類呢?解決方法之一是:引入一個抽象外觀類,客戶端針對抽象外觀類編程,而在運行時再肯定具體外觀類。更換具體外觀類時只需修改配置文件,無須修改源代碼,符合開閉原則。

總結

外觀模式是一種使用頻率很是高的設計模式,它經過引入一個外觀角色來簡化客戶端與子系統之間的交互,爲複雜的子系統調用提供一個統一的入口,使子系統與客戶端的耦合度下降,且客戶端調用很是方便。

外觀模式並不給系統增長任何新功能,它僅僅是簡化調用接口。在幾乎全部的軟件中都可以找到外觀模式的應用,如絕大多數B/S系統都有一個首頁或者導航頁面,大部分C/S系統都提供了菜單或者工具欄,在這裏,首頁和導航頁面就是B/S系統的外觀角色,而菜單和工具欄就是C/S系統的外觀角色,經過它們用戶能夠快速訪問子系統,下降了系統的複雜程度。全部涉及到與多個業務對象交互的場景均可以考慮使用外觀模式進行重構。

主要優勢
  1. 它對客戶端屏蔽了子系統組件,減小了客戶端所需處理的對象數目,並使得子系統使用起來更加容易。經過引入外觀模式,客戶端代碼將變得很簡單,與之關聯的對象也不多。
  2. 它實現了子系統與客戶端之間的鬆耦合關係,這使得子系統的變化不會影響到調用它的客戶端,只須要調整外觀類便可。
  3. 一個子系統的修改對其餘子系統沒有任何影響,並且子系統內部變化也不會影響到外觀對象。
主要缺點
  1. 不能很好地限制客戶端直接使用子系統類,若是對客戶端訪問子系統類作太多的限制則減小了可變性和靈活性。
  2. 若是設計不當,增長新的子系統可能須要修改外觀類的源代碼,違背了開閉原則。
適用場景
  1. 當要爲訪問一系列複雜的子系統提供一個簡單入口時可使用外觀模式。
  2. 客戶端程序與多個子系統之間存在很大的依賴性。引入外觀類能夠將子系統與客戶端解耦,從而提升子系統的獨立性和可移植性。
  3. 在層次化結構中,可使用外觀模式定義系統中每一層的入口,層與層之間不直接產生聯繫,而經過外觀類創建聯繫,下降層之間的耦合度。
相關文章
相關標籤/搜索