24種設計模式與6大原則
外部與一個子系統的通訊必須通過一個統一的門面(Facade)物件進行,這就是門面模式。
醫院的例子
用一個例子進行說明,如果把醫院作為一個子系統,按照部門職能,這個系統可以劃分為掛號、門診、劃價、化驗、收費、取藥等。看病的病人要與這些部門打交道,就如同一個子系統的客戶端與一個子系統的各個類打交道一樣,不是一件容易的事情。
首先病人必須先掛號,然後門診。如果醫生要求化驗,病人必須首先劃價,然後繳款,才能到化驗部門做化驗。化驗後,再回到門診室。
解決這種不便的方法便是引進門面模式。可以設定一個接待員的位置,由接待員負責代為掛號、劃價、繳費、取藥等。這個接待員就是門面模式的體現,病人只接觸接待員,由接待員負責與醫院的各個部門打交道。
什麼是門面模式
門面模式要求一個子系統的外部與其內部的通訊必須通過一個統一的門面(Facade)物件進行。門面模式提供一個高層次的介面,使得子系統更易於使用。
就如同醫院的接待員一樣,門面模式的門面類將客戶端與子系統的內部複雜性分隔開,使得客戶端只需要與門面物件打交道,而不需要與子系統內部的很多物件打交道。
二、 門面模式的結構
門面模式是物件的結構模式。門面模式沒有一個一般化的類圖描述,下圖演示了一個門面模式的示意性物件圖:
在這個物件圖中,出現了兩個角色:
門面(Facade)角色:客戶端可以呼叫這個角色的方法。此角色知曉相關的(一個或者多個)子系統的功能和責任。在正常情況下,本角色會將所有從客戶端發來的請求委派到相應的子系統去。
子系統(subsystem)角色:可以同時有一個或者多個子系統。每一個子系統都不是一個單獨的類,而是一個類的集合。每一個子系統都可以被客戶端直接呼叫,或者被門面角色呼叫。子系統並不知道門面的存在,對於子系統而言,門面僅僅是另外一個客戶端而已。
三、 門面模式的實現
一個系統可以有幾個門面類
【GOF】的書中指出:在門面模式中,通常只需要一個門面類,並且此門面類只有一個例項,換言之它是一個單例類。當然這並不意味著在整個系統裡只能有一個門面類,而僅僅是說對每一個子系統只有一個門面類。或者說,如果一個系統有好幾個子系統的話,每一個子系統有一個門面類,整個系統可以有數個門面類。
為子系統增加新行為
初學者往往以為通過繼承一個門面類便可在子系統中加入新的行為,這是錯誤的。門面模式的用意是為子系統提供一個集中化和簡化的溝通管道,而不能向子系統加入新的行為。
四、 在什麼情況下使用門面模式
- 為一個複雜子系統提供一個簡單介面
- 提高子系統的獨立性
- 在層次化結構中,可以使用Facade模式定義系統中每一層的入口。
五、 一個例子
我們考察一個保安系統的例子,以說明門面模式的功效。一個保安系統由兩個錄影機、三個電燈、一個遙感器和一個警報器組成。保安系統的操作人員需要經常將這些儀器啟動和關閉。
不使用門面模式的設計
首先,在不使用門面模式的情況下,操作這個保安系統的操作員必須直接操作所有的這些部件。下圖所示就是在不使用門面模式的情況下系統的設計圖。
可以看出,Client物件需要引用到所有的錄影機(Camera)、電燈(Light)、感應器(Sensor)和警報器(Alarm)物件。程式碼如下:
publicclass Camera
{
publicvoid TurnOn()
{
Console.WriteLine("Turning on the camera.");
}
publicvoid TurnOff()
{
Console.WriteLine("Turning off the camera.");
}
publicvoid Rotate(int degrees)
{
Console.WriteLine("Rotating the camera by {0} degrees.", degrees);
}
}
publicclass Light
{
publicvoid TurnOff()
{
Console.WriteLine("Turning on the light.");
}
publicvoid TurnOn()
{
Console.WriteLine("Turning off the light.");
}
publicvoid ChangeBulb()
{
Console.WriteLine("changing the light-bulb.");
}
}
publicclass Sensor
{
publicvoid Activate()
{
Console.WriteLine("Activating the sensor.");
}
publicvoid Deactivate()
{
Console.WriteLine("Deactivating the sensor.");
}
publicvoid Trigger()
{
Console.WriteLine("The sensor has triggered.");
}
}
publicclass Alarm
{
publicvoid Activate()
{
Console.WriteLine("Activating the alarm.");
}
publicvoid Deactivate()
{
Console.WriteLine("Deactivating the alarm.");
}
publicvoid Ring()
{
Console.WriteLine("Ringing the alarm.");
}
publicvoid StopRing()
{
Console.WriteLine("Stop the alarm.");
}
}
publicclass Client
{
privatestatic Camera camera1, camera2;
privatestatic Light light1, light2, light3;
privatestatic Sensor sensor;
privatestatic Alarm alarm;
static Client()
{
camera1 =new Camera();
camera2 =new Camera();
light1 =new Light();
light2 =new Light();
light3 =new Light();
sensor =new Sensor();
alarm =new Alarm();
}
publicstaticvoid Main( string[] args )
{
camera1.TurnOn();
camera2.TurnOn();
light1.TurnOn();
light2.TurnOn();
light3.TurnOn();
sensor.Activate();
alarm.Activate();
}
}
六、 使用門面模式的設計
一個合情合理的改進方法就是準備一個系統的控制檯,作為保安系統的使用者介面。如下圖所示:
程式程式碼如下:
using System;publicclass Camera
{
publicvoid TurnOn()
{
Console.WriteLine("Turning on the camera.");
}
publicvoid TurnOff()
{
Console.WriteLine("Turning off the camera.");
}
publicvoid Rotate(int degrees)
{
Console.WriteLine("Rotating the camera by {0} degrees.", degrees);
}
}
publicclass Light
{
publicvoid TurnOff()
{
Console.WriteLine("Turning on the light.");
}
publicvoid TurnOn()
{
Console.WriteLine("Turning off the light.");
}
publicvoid ChangeBulb()
{
Console.WriteLine("changing the light-bulb.");
}
}
publicclass Sensor
{
publicvoid Activate()
{
Console.WriteLine("Activating the sensor.");
}
publicvoid Deactivate()
{
Console.WriteLine("Deactivating the sensor.");
}
publicvoid Trigger()
{
Console.WriteLine("The sensor has triggered.");
}
}
publicclass Alarm
{
publicvoid Activate()
{
Console.WriteLine("Activating the alarm.");
}
publicvoid Deactivate()
{
Console.WriteLine("Deactivating the alarm.");
}
publicvoid Ring()
{
Console.WriteLine("Ringing the alarm.");
}
publicvoid StopRing()
{
Console.WriteLine("Stop the alarm.");
}
}
publicclass SecurityFacade