1 概述
1.1 引言
依据繁多权责准则,软件中将一个零碎划分为若干个子系统有利于升高整个零碎的复杂性,使客户类与子系统之间的通信和相互依赖关系达到最小,办法之一就是引入一个外观角色,为子系统的拜访提供一个简略而繁多的入口。外观模式通过引入一个新的外观角色来升高原有零碎的复杂度,同时升高客户类与子系统类的耦合度。
(这里的子系统是狭义的概念,能够是一个类,一个功能模块,零碎的一个组成部分或者一个残缺的零碎)
如果没有外观角色,每个客户端可能须要和多个子系统之间进行简单的交互,零碎的耦合度很大,简化示意图如下:
而引入外观角色后,客户端只需间接与外观角色交互,客户端与子系统之间的原有复杂度由外观角色实现,从而升高零碎耦合度,简化示意图如下:
外观模式要求一个子系统的内部与其外部的通信通过一个对立的外观角色进行,外观角色将客户端与子系统的外部复杂性分隔开,使得客户端只须要与外观角色打交道,而不须要与子系统外部的很多对象打交道。
1.2 定义
外观模式:内部与一个子系统的通信通过一个对立的外观角色进行,为子系统中的一组接口提供一个统一的入口。
外观模式定义了一个高层接口,这个接口使得这一子系统更加容易应用。
外观模式又叫门面模式,是一种对象结构型模式。
1.3 结构图
1.4 角色
- Facade(外观角色):在客户端能够调用这个角色的办法,在外观角色中能够晓得相干的一个或多个子系统的性能和责任,失常状况下将来自客户端的申请委派到对应的子系统中去,传递给相应的子系统对象解决
- SubSystem(子系统角色):每一个子系统是一个独自的类,也能够是一个类的汇合,实现子系统的性能。每一个子系统都能够被客户端间接调用,或者被外观角色调用,它解决由外观类传过来的申请,子系统并不知道外观类的存在,对于子系统而已,外观角色仅仅是另一个客户端
2 典型实现
2.1 步骤
- 定义子系统:首先定义子系统,实现一个繁多的性能,解决由客户端传来的申请
- 定义外观角色:外观角色能够晓得一个或多个子系统的性能和责任,将来自客户端的申请委派到对应的子系统去,外观角色对于子系统而言是另一个客户端
2.2 外观角色
通常实现如下:
class Facade
{private SubSystemA subSystemA = new SubSystemA();
private SubSystemB subSystemB = new SubSystemB();
private SubSystemC subSystemC = new SubSystemC();
public void method()
{subSystemA.method();
subSystemB.method();
subSystemC.method();}
}
class SubSystemA
{public void method()
{System.out.println("子系统 A");
}
}
class SubSystemB
{public void method()
{System.out.println("子系统 B");
}
}
class SubSystemC
{public void method()
{System.out.println("子系统 C");
}
}
3 实例
设计一个文件加密零碎,加密流程包含三局部:读取文件,加密文件,保留文件。这三个操作绝对独立,并且封装在三个不同的类中,应用外观模式设计该零碎。
子系统类:
-
FileReader
:文件读取类 -
Encrypt
:文件加密类 -
FileWriter
:文件保留类
外观角色类:Facade
。
代码如下:
public class Test
{public static void main(String[] args) {Facade facade = new Facade();
facade.fileEncrypt("111");
}
}
class FileReader
{public void read(String name)
{System.out.println("读取文件"+name);
}
}
class Encrypt
{public void encrypt(String name)
{System.out.println("加密文件"+name);
}
}
class FileWriter
{public void write(String name)
{System.out.println("保留文件"+name);
}
}
class Facade
{private FileReader reader = new FileReader();
private Encrypt encrypt = new Encrypt();
private FileWriter writer = new FileWriter();
public void fileEncrypt(String name)
{reader.read(name);
encrypt.encrypt(name);
writer.write(name);
}
}
这里的例子比较简单,其实就是将读取,加密以及保留操作用外观角色包装起来,不便客户端调用。
4 引入形象外观类
4.1 为什么须要引入形象外观类?
在规范的外观模式构造中,如果须要减少,删除或批改外观类交互的子系统类,必须批改外观类或客户端的源代码,这将违反开闭准则,比方下面的例子中须要更换一种加密办法,也就是换一个加密类,这样须要间接批改外观类。
能够通过引入形象外观类来解决该问题,引入后,客户端能够针对形象外观类进行编程,对于新的业务需要不须要批改原有的外
观类,只须要新增一个对应的具体外观类即可。
4.2 如何引入?
首先定义形象外观类,接着具体外观类继承或者实线形象外观类即可。客户端针对形象外观类进行编程,在运行时再确定具体的外观类,比方在下面例子的根底上批改加密办法,首先定义形象外观类(这里是接口,只有一个加密办法):
interface AbstractFacade
{void encrypt(String name);
}
接着定义具体外观类:
class Facade1 implements AbstractFacade
{private FileReader reader = new FileReader();
private Encrypt1 encrypt1 = new Encrypt1();
private FileWriter writer = new FileWriter();
@Override
public void encrypt(String name)
{reader.read(name);
encrypt1.encrypt(name);
writer.write(name);
}
}
class Facade2 implements AbstractFacade
{private FileReader reader = new FileReader();
private Encrypt2 encrypt2 = new Encrypt2();
private FileWriter writer = new FileWriter();
@Override
public void encrypt(String name)
{reader.read(name);
encrypt2.encrypt(name);
writer.write(name);
}
}
这两个类除了加密办法不一样其余都一样,测试:
AbstractFacade facade = new Facade1();
facade.encrypt("111");
facade = new Facade2();
facade.encrypt("222");
引入形象外观类后,客户端针对形象外观类进行编程,运行时确定具体外观类,输入如下:
5 注意事项
- 外观单例:很多状况下为了节约系统资源,零碎只须要一个外观类的实例,也就是外观类能够是一个单例类,这样能够升高系统资源的耗费
- 多个外观类:在一个零碎中能够设计多个外观类,每个外观类负责和一些特定子对象交互,向客户端提供相应业务性能
- 不要通过外观类减少新行为:外观模式的用意是为子系统提供一个集中简化的沟通渠道,而不是向子系统中减少新行为,新行为的减少应该通过批改原有子系统类或减少新的子系统类来实现而不是通过外观类实现
6 次要长处
- 简化解决:对客户端屏蔽了子系统组件,缩小了客户端所需解决的对象数目并使得子系统应用起来更加容易,引入外观模式后客户端代码将简化
- 松耦合:实现了子系统于客户端之间松耦合关系,使得子系统的变动不会影响到客户端,只需批改外观类
- 子系统批改灵便:一个子系统的批改对其余子系统没有影响,而且子系统外部变动也不会影响外观对象
- 惟一入口:只提供了一个拜访子系统的惟一入口,但不会影响客户端间接应用子系统类
7 次要毛病
- 不能限度客户端应用子系统:外观模式不能很好地限度客户端间接应用子系统,如果客户端对拜访子系统做太多的限度就会缩小可变性与灵活性
- 可能须要批改外观类:如果设计不当,减少新的子系统可能须要外观类,违反 OCP
8 实用场景
- 当要为拜访一系列简单的子系统提供一个简略的入口时
- 客户端与多个子系统存在很大依赖性
- 层次化构造中,能够应用外观模式定义零碎中每一层的入口,层与层之间不间接产生分割,而通过外观类建立联系,升高层之间的耦合度
9 总结