最近准备好好研究下Retrifot的源代码 , 因为Retrofit的主要业务用到了外观模式,正好以前没有仔细研究过这种设计模式,同时也是为了降低研究Retrofit源码的难度,所以做下关于外观设计模式的功课。为了方便看官理解,无关的代码部分尽可能的使用了伪代码。
进入正题 , 软件开发中时常出现,需要与多个复杂子系统进行交互的情况,倘若直接与各个子系统进行交互必定会出现较高的耦合性。如果存在某一个中间对象专门负责与各个子系统进行交互,而第三方只需要与该中间对象交互即可,这便是外观模式的由来。
如上图,没有使用外观模式之前,各个系统之间的交互的复杂性,而使用了外观模式后,使用Facade中间对象与各个子系统进行交互,整个系统的耦合性一下就降低了。
标准外观模式:
外观模式中所指的子系统是一个广义的概念,它可以是一个类、一个功能模块、系统的一个组成部分或者一个完整的系统。子系统类通常是一些业务类,实现了一些具体的、独立的业务功能。
以文件加解密为例上实例代码,文件加解密可以分为三个模块, 读取文件,加解密文件,写入文件。其UML图如下:
class FileReader
{
public string Read(string fileNameSrc)
{
......
}
}
class CipherMachine
{
public string Encrypt(string plainText)
{
Console.Write("数据加密,将明文转换为密文:");
......
return es;
}
class FileWriter
{
public void Write(string encryptStr,string fileNameDes)
{
......
}
}
重点在EncryptFacade上,用于与各个子系统进行直接交互.
class EncryptFacade
{
//维持对其他对象的引用
private FileReader reader;
private CipherMachine cipher;
private FileWriter writer;
public EncryptFacade()
{
reader = new FileReader();
cipher = new CipherMachine();
writer = new FileWriter();
}
//调用其他对象的业务方法
public void FileEncrypt(string fileNameSrc, string fileNameDes)
{
string plainStr = reader.Read(fileNameSrc);
string encryptStr = cipher.Encrypt(plainStr);
writer.Write(encryptStr, fileNameDes);
}
}
class Program
{
static void Main(string[] args)
{
EncryptFacade ef = new EncryptFacade();
ef.FileEncrypt("src.txt", "des.txt");
Console.Read();
}
}
抽象外观模式
在标准的外观模式中,如果需要增加、删除或更换与外观类交互的子系统类,必须修改外观类或客户端的源代码,这将违背开闭原则,因此可以通过引入抽象外观类来对系统进行改进,在一定程度上可以解决该问题。
在引入抽象外观类之后,客户端可以针对抽象外观类进行编程,对于新的业务需求,不需要修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改任何源代码并更换外观类的目的。
如,现在我们不满意原有的那种加解密方式了,想要使用一种新的加解密方式替代。
使用抽象外观模式更改后的UML图.
其中,FileReader和FileWriter使用的仍然是之前的类,这里只不过是增加了一个新的加解密类NewCipherMachine,一个新的中间对象NewEncryptFacade,一个抽象类AbstractEncryptFacade.
abstract class AbstractEncryptFacade
{
public abstract void FileEncrypt(string fileNameSrc, string fileNameDes);
}
class NewEncryptFacade extends 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);
}
}
class NewCipherMachine
{
public string Encrypt(string plainText)
{
Console.Write("数据加密,将明文转换为密文:");
。。。。。。
return es;
}
}
这里使用配置文件,通过在代码中选择加载配置文件,从而使用对应的Facade对象,以此来调用不同的加解密类。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="facade" value="FacadeSample.NewEncryptFacade"/>
</appSettings>
</configuration>
class Program
{
static void Main(string[] args)
{
AbstractEncryptFacade ef; //针对抽象外观类编程
//读取配置文件
string facadeString = ConfigurationManager.AppSettings["facade"];
//反射生成对象
ef = (AbstractEncryptFacade)。。。。。。(facadeString);
ef.FileEncrypt("src.txt", "des.txt");
Console.Read();
}
}
总结:
自我感觉,即便是抽象外观模式,做的还是不够好,
每次要增加一个新的功能或者方法时,就需要增加一个新的功能类以及一个新的中间Facade类,虽然通过配置文件可以选择调用不同的功能类,但是感觉还是不够。