深入淺出外觀模式(三)
4. 抽象外觀類
在標準的外觀模式結構圖中,如果需要增加、刪除或更換與外觀類互動的子系統類,必須修改外觀類或客戶端的原始碼,這將違背開閉原則,因此可以通過引入抽象外觀類來對系統進行改進,在一定程度上可以解決該問題。在引入抽象外觀類之後,客戶端可以針對抽象外觀類進行程式設計,對於新的業務需求,不需要修改原有外觀類,而對應增加一個新的具體外觀類,由新的具體外觀類來關聯新的子系統物件,同時通過修改配置檔案來達到不修改任何原始碼並更換外觀類的目的。
下面通過一個具體例項來學習如何使用抽象外觀類:
如果在應用例項“檔案加密模組”中需要更換一個加密類,不再使用原有的基於求模運算的加密類CipherMachine
using System; namespace FacadeSample { class NewCipherMachine { public string Encrypt(string plainText) { Console.Write("資料加密,將明文轉換為密文:"); string es = ""; int key = 10;//設定金鑰,移位數為10 char[] chars = plainText.ToCharArray(); foreach(char ch in chars) { int temp = Convert.ToInt32(ch); //小寫字母移位 if (ch >= 'a' && ch <= 'z') { temp += key % 26; if (temp > 122) temp -= 26; if (temp < 97) temp += 26; } //大寫字母移位 if (ch >= 'A' && ch <= 'Z') { temp += key % 26; if (temp > 90) temp -= 26; if (temp < 65) temp += 26; } es += ((char)temp).ToString(); } Console.WriteLine(es); return es; } } }
如果不增加新的外觀類,只能通過修改原有外觀類EncryptFacade的原始碼來實現加密類的更換,將原有的對CipherMachine型別物件的引用改為對NewCipherMachine型別物件的引用,這違背了開閉原則,因此需要通過增加新的外觀類來實現對子系統物件引用的改變。
如果增加一個新的外觀類NewEncryptFacade來與FileReader類、FileWriter類以及新增加的NewCipherMachine類進行互動,雖然原有系統類庫無須做任何修改,但是因為客戶端程式碼中原來針對EncryptFacade類進行程式設計,現在需要改為NewEncryptFacade類,因此需要修改客戶端原始碼。
如何在不修改客戶端程式碼的前提下使用新的外觀類呢?解決方法之一是:引入一個抽象外觀類,客戶端針對抽象外觀類程式設計,而在執行時再確定具體外觀類,引入抽象外觀類之後的檔案加密模組結構圖如圖5所示:
圖5 引入抽象外觀類之後的檔案加密模組結構圖
在圖5中,客戶類Client針對抽象外觀類AbstractEncryptFacade進行程式設計,AbstractEncryptFacade程式碼如下:
namespace FacadeSample
{
abstract class AbstractEncryptFacade
{
public abstract void FileEncrypt(string fileNameSrc, string fileNameDes);
}
}
新增具體加密外觀類NewEncryptFacade程式碼如下:
namespace FacadeSample
{
class NewEncryptFacade : AbstractEncryptFacade
{
private FileReader reader;
private NewCipherMachine cipher;
private FileWriter writer;
public NewEncryptFacade()
{
reader = new FileReader();
cipher = new NewCipherMachine();
writer = new FileWriter();
}
public override void FileEncrypt(string fileNameSrc, string fileNameDes)
{
string plainStr = reader.Read(fileNameSrc);
string encryptStr = cipher.Encrypt(plainStr);
writer.Write(encryptStr, fileNameDes);
}
}
}
配置檔案App.config中儲存了具體外觀類的類名,程式碼如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="facade" value="FacadeSample.NewEncryptFacade"/>
</appSettings>
</configuration>
客戶端測試程式碼修改如下:
using System;
using System.Configuration;
using System.Reflection;
namespace FacadeSample
{
class Program
{
static void Main(string[] args)
{
AbstractEncryptFacade ef; //針對抽象外觀類程式設計
//讀取配置檔案
string facadeString = ConfigurationManager.AppSettings["facade"];
//反射生成物件
ef = (AbstractEncryptFacade)Assembly.Load("FacadeSample"). CreateInstance (facadeString);
ef.FileEncrypt("src.txt", "des.txt");
Console.Read();
}
}
}
編譯並執行程式,輸出結果如下:
讀取檔案,獲取明文:Hello world! 資料加密,將明文轉換為密文:Rovvy gybvn! 儲存密文,寫入檔案。 |
原有外觀類EncryptFacade也需作為抽象外觀類AbstractEncryptFacade類的子類,更換具體外觀類時只需修改配置檔案,無須修改原始碼,符合開閉原則。
5. 外觀模式效果與適用場景
外觀模式是一種使用頻率非常高的設計模式,它通過引入一個外觀角色來簡化客戶端與子系統之間的互動,為複雜的子系統呼叫提供一個統一的入口,使子系統與客戶端的耦合度降低,且客戶端呼叫非常方便。外觀模式並不給系統增加任何新功能,它僅僅是簡化呼叫介面。在幾乎所有的軟體中都能夠找到外觀模式的應用,如絕大多數B/S系統都有一個首頁或者導航頁面,大部分C/S系統都提供了選單或者工具欄,在這裡,首頁和導航頁面就是B/S系統的外觀角色,而選單和工具欄就是C/S系統的外觀角色,通過它們使用者可以快速訪問子系統,降低了系統的複雜程度。所有涉及到與多個業務物件互動的場景都可以考慮使用外觀模式進行重構。
5.1 模式優點
外觀模式的主要優點如下:
(1) 它對客戶端遮蔽了子系統元件,減少了客戶端所需處理的物件數目,並使得子系統使用起來更加容易。通過引入外觀模式,客戶端程式碼將變得很簡單,與之關聯的物件也很少。
(2) 它實現了子系統與客戶端之間的鬆耦合關係,這使得子系統的變化不會影響到呼叫它的客戶端,只需要調整外觀類即可。
(3) 一個子系統的修改對其他子系統沒有任何影響,而且子系統內部變化也不會影響到外觀物件。
5.2 模式缺點
外觀模式的主要缺點如下:
(1) 不能很好地限制客戶端直接使用子系統類,如果對客戶端訪問子系統類做太多的限制則減少了可變性和靈活 性。
(2) 如果設計不當,增加新的子系統可能需要修改外觀類的原始碼,違背了開閉原則。
5.3 模式適用場景
在以下情況下可以考慮使用外觀模式:
(1) 當要為訪問一系列複雜的子系統提供一個簡單入口時可以使用外觀模式。
(2) 客戶端程式與多個子系統之間存在很大的依賴性。引入外觀類可以將子系統與客戶端解耦,從而提高子系統的獨立性和可移植性。
(3) 在層次化結構中,可以使用外觀模式定義系統中每一層的入口,層與層之間不直接產生聯絡,而通過外觀類建立聯絡,降低層之間的耦合度。