外观模式(Facade pattern)

    某软件公司欲开发一个可应用于多个软件的文件加密模块,该模块可以对文件中的数据进行加密并将加密之后的数据存储在一个新文件中,具体的流程包括三个部分,分别是读取源文件、加密、保存加密之后的文件,其中,读取文件和保存文件使用流来实现,加密操作通过求模运算实现。这三个操作相对独立,为了实现代码的独立重用,让设计更符合单一职责原则,这三个操作的业务代码封装在三个不同的类中。
    现使用外观模式设计该文件加密模块。

    (1) FileReader:文件读取类,充当子系统类。

    (2) CipherMachine:数据加密类,充当子系统类。

    1. using System;
    2. using System.Text;
    3. namespace FacadeSample
    4. {
    5. class CipherMachine
    6. {
    7. public string Encrypt(string plainText)
    8. {
    9. Console.Write("数据加密,将明文转换为密文:");
    10. string es = "";
    11. char[] chars = plainText.ToCharArray();
    12. foreach(char ch in chars)
    13. {
    14. string c = (ch % 7).ToString();
    15. es += c;
    16. }
    17. Console.WriteLine(es);
    18. return es;
    19. }
    20. }
    21. }

    (3) FileWriter:文件保存类,充当子系统类。

    1. //FileWriter.cs
    2. using System;
    3. using System.IO;
    4. using System.Text;
    5. namespace FacadeSample
    6. {
    7. class FileWriter
    8. {
    9. public void Write(string encryptStr,string fileNameDes)
    10. {
    11. Console.WriteLine("保存密文,写入文件。");
    12. FileStream fs = null;
    13. try
    14. {
    15. byte[] str = Encoding.Default.GetBytes(encryptStr);
    16. fs.Write(str,0,str.Length);
    17. fs.Close();
    18. }
    19. catch(FileNotFoundException e)
    20. {
    21. Console.WriteLine("文件不存在!");
    22. }
    23. catch(IOException e)
    24. {
    25. Console.WriteLine(e.Message);
    26. Console.WriteLine("文件操作错误!");
    27. }
    28. }
    29. }
    30. }

    (5) Program:客户端测试类

    1. //Program.cs
    2. using System;
    3. namespace FacadeSample
    4. {
    5. class Program
    6. {
    7. static void Main(string[] args)
    8. {
    9. EncryptFacade ef = new EncryptFacade();
    10. ef.FileEncrypt("src.txt", "des.txt");
    11. Console.Read();
    12. }
    13. }
    14. }
    1. 读取文件,获取明文:Hello world!
    2. 数据加密,将明文转换为密文:233364062325
    3. 保存密文,写入文件。

    在标准的外观模式结构图中,如果需要增加、删除或更换与外观类交互的子系统类,必须修改外观类或客户端的源代码,这将违背开闭原则,因此可以通过引入抽象外观类来对系统进行改进,在一定程度上可以解决该问题。在引入抽象外观类之后,客户端可以针对抽象外观类进行编程,对于新的业务需求,不需要修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改任何源代码并更换外观类的目的。

    下面通过一个具体实例来学习如何使用抽象外观类:

    如果在应用实例“文件加密模块”中需要更换一个加密类,不再使用原有的基于求模运算的加密类CipherMachine,而改为基于移位运算的新加密类NewCipherMachine,其代码如下:

    如果增加一个新的外观类NewEncryptFacade来与FileReader类、FileWriter类以及新增加的NewCipherMachine类进行交互,虽然原有系统类库无须做任何修改,但是因为客户端代码中原来针对EncryptFacade类进行编程,现在需要改为NewEncryptFacade类,因此需要修改客户端源代码。

    如何在不修改客户端代码的前提下使用新的外观类呢?解决方法之一是:引入一个抽象外观类,客户端针对抽象外观类编程,而在运行时再确定具体外观类,引入抽象外观类之后的文件加密模块结构图如图5所示:

    客户类Client针对抽象外观类AbstractEncryptFacade进行编程,AbstractEncryptFacade代码如下:

    1. namespace FacadeSample
    2. {
    3. abstract class AbstractEncryptFacade
    4. {
    5. public abstract void FileEncrypt(string fileNameSrc, string fileNameDes);
    6. }
    7. }

    新增具体加密外观类NewEncryptFacade代码如下:

    1. namespace FacadeSample
    2. class NewEncryptFacade : AbstractEncryptFacade
    3. {
    4. private FileReader reader;
    5. private NewCipherMachine cipher;
    6. private FileWriter writer;
    7. public NewEncryptFacade()
    8. {
    9. reader = new FileReader();
    10. cipher = new NewCipherMachine();
    11. writer = new FileWriter();
    12. }
    13. public override void FileEncrypt(string fileNameSrc, string fileNameDes)
    14. {
    15. string plainStr = reader.Read(fileNameSrc);
    16. string encryptStr = cipher.Encrypt(plainStr);
    17. writer.Write(encryptStr, fileNameDes);
    18. }
    19. }
    20. }
    1. using System;
    2. using System.Configuration;
    3. using System.Reflection;
    4. namespace FacadeSample
    5. {
    6. class Program
    7. {
    8. static void Main(string[] args)
    9. {
    10. AbstractEncryptFacade ef; //针对抽象外观类编程
    11. //读取配置文件
    12. string facadeString = ConfigurationManager.AppSettings["facade"];
    13. //反射生成对象
    14. ef = (AbstractEncryptFacade)Assembly.Load("FacadeSample"). CreateInstance (facadeString);
    15. ef.FileEncrypt("src.txt", "des.txt");
    16. Console.Read();
    17. }
    18. }

    在以下情况下可以考虑使用外观模式:

    1. 当要为访问一系列复杂的子系统提供一个简单入口时可以使用外观模式。
    2. 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性。