外观模式(门面模式)
定义:它为子系统中的一组接口提供一个统一的高层接口。这一接口使得子系统更加容易使用
类型:结构型
适用场景:
- 子系统越来越复杂,增加外观模式提供简单的接口调用
- 构建多层系统接口,利用外观对象作为每层的入口,简化层间调用
优点:
- 简化了调用过程,无需深入了解子系统,防止带来的风险
- 减少系统依赖,松散耦合
- 更好的划分访问层次
- 符合迪米特法则(最少知道原则)
缺点:
- 增加子系统,扩展子系统行为容易引入风险
- 不符合开闭原则
UML类图:
Facade:门面角色,客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本橘色会将所有从客户端发来的请求委托到相应的子系统,也就是说该角色并没有实际的业务逻辑,而是一个委托类
Subsystem:子系统角色,可以同时有一个或者多个子系统。每一个子系统都不是单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已
示例
ModuleA,ModuleB,ModuleC均是独立的功能模块,现在把他们都看作是子系统的内部结构组成,提供个Facade门面供客户端调用
- 模块A、模块B、模块C
public class ModuleA {
//示意方法
public void testA() {
System.out.println("调用ModuleA中的testA方法");
}
}
public class ModuleB {
//示意方法
public void testB() {
System.out.println("调用ModuleB中的testB方法");
}
}
public class ModuleC {
//示意方法
public void testC() {
System.out.println("调用ModuleC中的testC方法");
}
}
- Facade门面(对子系统的各个功能模块进行封装、子系统的功能模块之间的调用逻辑也可以在这里完成)
public class Facade {
//示意方法,满足客户端需要的功能
public void test(){
ModuleA a = new ModuleA();
a.testA();
ModuleB b = new ModuleB();
b.testB();
ModuleC c = new ModuleC();
c.testC();
}
}
- 客户端
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.test();
}
}
Facade类其实相当于A、B、C模块的外观界面,有了这个Facade类,那么客户端就不需要亲自调用子系统中的A、B、C模块了,也不需要知道系统内部的实现细节,甚至都不需要知道A、B、C模块的存在,客户端只需要跟Facade类交互就好了,从而更好地实现了客户端和子系统中A、B、C模块的解耦,让客户端更容易地使用系统。
- 在日常J2EE开发中,我们最常与controller/service/dao打交道,这就是典型的外观模式的实现,这里以一个订单业务举例
图中的Dao层的OrderDao、OrderLogDao可以看做是子系统的组成部分,主要围绕的是数据库的增删改查,而Service层中的OrderServiceImpl就是我们外观对象实现,通常我们会根据具体的业务逻辑去调用dao层提供的api,这里就提供了一个createOrder
创建订单的外观方法给Contorller层(客户端)进行调用,客户端根本不需要了解具体订单生成逻辑就可以完成订单的创建。这里对外观类进行了接口抽象,客户端依赖的是OrderService接口而不是OrderServiceImpl实现类,这样就解除了客户端与子系统的依赖,而让客户端只依赖于外观接口,这是一个优秀的解耦实践。
相关的设计模式
-
外观模式和中介者模式
外观模式关注的是外界和子系统之间的交互,而中介者模式关注的是子系统之间的内部交互
-
外观模式和单例模式
通常可以把外观模式中的外观对象做成是单例模式
-
外观模式和抽象工厂模式
外观类可以通过抽象工厂获取子系统的实例,这样子系统可以对外观类进行屏蔽
-
外观模式和代理模式
代理对象代表一个单一对象而外观对象代表一个子系统,代理的客户对象无法直接访问对象,由代理提供单独的目标对象的访问,而通常外观对象提供对子系统各元件功能的简化的共同层次的调用接口。代理是一种原来对象的代表,其他需要与这个对象打交道的操作都是和这个代表交涉的
-
外观模式和适配器模式
外观与适配器都是对现存系统的封装。外观定义的新的接口,而适配器则是复用一个原有的接口,适配器是使两个已有的接口协同工作,而外观则是为现存系统提供一个更为方便的访问接口。如果硬要说外观是适配,那么适配器有用来适配对象的,而外观是用来适配整个子系统的。也就是说,外观所针对的对象的粒度更大
使用典范
- SLF4J日志框架
- tomcat中的RequestFacade
- java.lang.Class