这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是第三方 SDK 基本很大概率都会使用外观模式。通过一个外观类使得整个子系统只有一个统一的高层的接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节。当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块、ImageLoader 模块等。其实我们在开发过程中可能已经使用过很多次外观模式,只是没有从理论层面去了解它。
转载请注明出处:http://blog.csdn.net/self_study/article/details/51931196。
PS:对技术感兴趣的同鞋加群544645972一起交流。
设计模式总目录
特点
外观模式提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。
外观模式的使用场景:
- 为一个复杂子系统提供一个简单接口。Facade 可以提供一个简单统一的接口,对外隐藏子系统的具体实现、隔离变化。
- 当需要构建一个层次结构的子系统时,使用 Facade 模式定义子系统中每层的入口点。如果子系统之间是相互依赖,你可以让他们仅通过 Facade 接口进行通信,从而简化了它们之间的依赖关系。
外观模式简化了接口,并且同时允许我们让客户端和子系统之间避免紧耦合。
UML类图
外观模式没有一个一般化的类图描述,我们先用一个结构图来说明:
根据结构图抽象出一个类图:
外观模式有两个角色:
- Facade 角色:系统对外的统一接口,客户端连接子系统功能的入口。
- 子系统角色(SubSystem)角色:可以同时有一个或者多个子系统,每个子系统都不是一个单独的类,而是一个类的集合。每个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。
我们根据上面的 uml 类图可以写出外观模式的通用代码:
子系统:
public class SystemA {
public void operation1(){
System.out.print("SystemA:operation1\n");
}
public void operation2(){
System.out.print("SystemA:operation2\n");
}
public void operation3(){
System.out.print("SystemA:operation3\n");
}
}
public class SystemB {
public void operation1(){
System.out.print("SystemB:operation1\n");
}
public void operation2(){
System.out.print("SystemB:operation2\n");
}
public void operation3(){
System.out.print("SystemB:operation3\n");
}
}
public class SystemC {
public void operation1(){
System.out.print("SystemC:operation1\n");
}
public void operation2(){
System.out.print("SystemC:operation2\n");
}
public void operation3(){
System.out.print("SystemC:operation3\n");
}
}
然后是外观对象 IFacade.class
public interface IFacade {
void operationA();
void operationB();
void operationC();
}
Facade.class
public class Facade implements IFacade{
private SystemA systemA = new SystemA();
private SystemB systemB = new SystemB();
private SystemC systemC = new SystemC();
@Override
public void operationA() {
systemA.operation1();
systemB.operation2();
systemC.operation3();
}
@Override
public void operationB() {
systemA.operation2();
systemB.operation1();
systemC.operation3();
}
@Override
public void operationC() {
systemC.operation1();
systemB.operation2();
systemA.operation3();
}
}
最后的测试代码:
public static void main(String args[]) {
IFacade facade = new Facade();
facade.operationA();
facade.operationB();
facade.operationC();
}
结果输出如下:
示例与源码
这里直接展示一段简单电脑启动时的伪代码来表示即可:
/* Complex parts */
class CPU {
public void freeze() { ... }
public void jump(long position) { ... }
public void execute() { ... }
}
class Memory {
public void load(long position, byte[] data) { ... }
}
class HardDrive {
public byte[] read(long lba, int size) { ... }
}
/* Facade */
class ComputerFacade {
private CPU processor;
private Memory ram;
private HardDrive hd;
public ComputerFacade() {
this.processor = new CPU();
this.ram = new Memory();
this.hd = new HardDrive();
}
public void start() {
processor.freeze();
ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE));
processor.jump(BOOT_ADDRESS);
processor.execute();
}
}
/* Client */
class You {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.start();
}
}
总结
外观模式是一个高频率使用的设计模式,它的精髓就在于封装二字。通过一个高层次结构为用户提供统一的 API 入口,使得用户通过一个类型就基本能够操作整个系统,这样减少了用户的使用成本,也能够提升系统的灵活性。
外观类遵循了一个很重要设计模式原则:迪米特原则(最少知识原则),它让客户端依赖于最少的类,直接依赖外观类而不是依赖于所有的子系统类。
优点:
- 对客户程序隐藏子系统细节,因而减少了客户对于子系统的耦合,能够拥抱变化;
- 外观类对子系统的接口封装,使得系统更易于使用;
- 更好的划分访问层次,通过合理使用Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到外观类中,这样既方便客户端使用,也很好地隐藏了内部的细节。
缺点:
- 外观类接口膨胀,由于子系统的接口都由外观类统一对外暴露,使得外观类的 API 接口较多,在一定程度上增加了用户使用成本;
- 外观类没有遵循开闭原则,当业务出现变更时,可能需要直接修改外观类。
适配器 VS 装饰者 VS 桥接 VS 代理 VS 外观
这几个都是结构型设计模式,他们有些类似,在实际使用过程中也容易搞混,我们在这就给他们做一个对比:
适配器模式
适配器模式和其他三个设计模式一般不容易搞混,它的作用是将原来不兼容的两个类融合在一起,uml 图也和其他的差别很大。
装饰者模式
装饰者模式结构上类似于代理模式,但是和代理模式的目的是不一样的,装饰者是用来动态地给一个对象添加一些额外的职责,装饰者模式为对象加上行为,而代理则是控制访问。
桥接模式
桥接模式的目的是为了将抽象部分与实现部分分离,使他们都可以独立地进行变化,所以说他们两个部分是独立的,没有实现自同一个接口,这是桥接模式与代理模式,装饰者模式的区别。
代理模式
代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理的方式有很多种,比如远程代理和虚拟代理等,这个在上面有,这里就不说了,而装饰者模式则是为了扩展对象。
外观模式
外观模式提供一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
适配器模式将一个或多个类接口变成客户端所期望的一个接口,虽然大多数资料所采用的例子中适配器只适配一个类,但是你可以适配许多类来提供一个接口让客户端访问;类似的,外观模式 也可以只针对一个拥有复杂接口的类提供简化的接口,两种模式的差异,不在于他们“包装”了几个类,而是在于它们的意图。适配器模式 的意图是,“改变”接口符合客户的期望;而外观模式的意图是,提供子系统的一个简化接口。
源码下载
https://github.com/zhaozepeng/Design-Patterns/tree/master/FacadePattern