引子
我们先来讲一个故事,比如我现在要组装一台电脑。
方案一:去电子市场买cpu,内存条,显卡,磁盘等所有用到的部件,然后再进行组装。但是这个方案的问题在于,要对这些部件有所了解,选择性能好的,考虑不同部件的兼容性问题等。
方案二:自己组装太麻烦了,找个装机公司吧,然后说自己的需求,之后就等着拿电脑就完事了。
假设我们把电子市场看成一个系统,把卖配件的公司看成不同的子系统,那么客户端(我)需要完成一个目标(组装一台电脑),需要调用一个系统的多个子系统A,B,C…,需要知道不同子系统的功能,还需要如何组合这个子系统才能完成。
方案二提供了一个很简单的方法,能让客户端实现相同的功能却不需要和系统中的多个模块交互。
其实方案二中的装机公司就是外观角色,用户只需要和外观类交互即可,用户和子系统的复杂关系交给外观角色来控制,这就是要讲的外观模式了。
外观模式简介
定义:为系统中的一组接口(可以理解为多个子系统)提供了一个统一的入口。外观模式提供了一个高层接口,目的是访问其他接口可以通过它统一访问。
表现:一个子系统外部和其内部的通信交给外观类来进行,将客户类和内部子系统分隔开,客户类只需要和外观类交流即可,不需要和内部的很多对象进行交流。
目的:降低了类之间的耦合,降低系统复杂度。
模式结构:
结构图说明:
Facade:外观角色,客户可以调用它的方法。再外观角色中知道相关子系统的功能和职责,外观角色负责将客户端发来的请求委派到不同的子系统中。
SubSystem:子系统角色,系统中存在一个或多个子系统角色,每一个子系统都是一个类集合,实现了特定的功能。
举例说明
就以开始讲的组装电脑为例子吧。
不同的组件:
class Memory {
public void methodA() {
System.out.println("组装内存。。。");
}
}
class CPU {
public void methodB() {
System.out.println("组装CPU");
}
}
装机公司:
public class BuildCompany {
private Memory m = new Memory();
private CPU cpu = new CPU();
public void build() {
System.out.println("开始组装电脑。。");
m.methodA();
cpu.methodB();
}
}
客户端测试:
public class FacaClient {
public static void main(String[] args) {
BuildCompany buildCompany = new BuildCompany();
buildCompany.build();
}
}
很简单的例子,可以看出了外观类可以处理客户端的请求即可。
优缺点和场景
优点:
- 对客户端屏蔽了子系统组件,减少了客户端需要处理对象的数目,使子系统的使用更方便
- 实现了子系统和客户端的松耦合,子系统的变化不会影响调用它的客户端,自需要调整外观类。
缺点:
- 如果设计存在问题,当增加新的子系统时需要修改外观类
- 外观类并不能完全限制客户端访问子系统
使用场景:
- 客户端和许多的子系统存在依赖关系
- 层次化结构中,在不同层之间建立外观类
举例:网页中的导航、客户端中的菜单栏等
扩展
- 一个系统可以有多个外观类,每个外观类处理特定的子系统。
- 一个外观类不能通过子类继承的方式增加新功能。因为外观模式本身的目的就是简化交互的模式,不是用来添加新功能的。所以如果需要添加新功能可以通过修改子系统或添加新的子系统来实现。
- 符合“迪米特法则”的最少知道原则,尽可能少的与不同的子系统打交道,而是通过外观类来交互。
上面也有讲到,如果需要增加、删除、更换与外观类直接交互的子系统的化,就必须要修改外观类或客户端代码类,违背了开闭原则(对扩展开发,对修改关闭)。那么可以引入一个抽象外观类来一定程度上解决这个问题。
如果有新的业务需求,不需要修改原有的外观类,只要增加新的具体外观类,又新的具体外观类来关联新的子系统对象,通过修改配置文件就可以做到不更改源代码来更换外观类的目的。
扩展的结构图:
总体来说,外观模式还是很好理解的,目的就是减少耦合和系统复杂程度。生活中也经常可以遇到外观模式的一些思想体现,大家可以留心观察一下。
如果有什么建议或意见,欢迎大家留言讨论~