设计模式 (十一) 外观模式
目录
一、定义
二、角色分析
三、外观模式案例分析
四、外观模式应用分析
五、总结
一、定义
外观模式,也叫门面模式或者过程模式,是一种比较常用的封装模式,其定义如下:
要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,屏蔽内部子系统的实现细节,使得子系统更易于使用。外观模式也属于结构性模式。
门面模式注重"统一的对象",也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为发生。
- 传统方式原理图
客户端直接依赖各个子系统,这样子系统发生一点变化,客户端就要改,不符合开闭原则。如下图:客户端直接调用各个子系统的接口。
- 外观模式原理图
可以看到,使用外观模式后,客户端不直接调用各个子系统的接口,而是调用外观模式提供的简化之后的接口,当然客户端还是可以调用各个子系统的接口的。在客户端和各个子系统中增加一个高层接口,即如上图的Facade外观类,使得客户端直接与外观类进行关联,而不是与各个子系统关联,无须关心子系统实现细节。
二、角色分析
我们首先明确一下门面模式的角色:
- Facade外观角色:就是上面所说的高层接口,为客户端提供统一的调用接口。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就是说该角色没有实际的业务逻辑,只是一个委托类。请求只会转发到外观类,然后由外观类决定调用哪一个具体的子系统处理请求。(有点类似代理的概念,让代理给你转发请求);
- SubSystem子系统角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已;
三、外观模式案例分析
下面我们以一个生活中再熟悉不过的例子来说明外观模式,我们每天做饭的流程基本如下:
买菜 -> 洗菜 -> 打开抽油烟机 -> 烹饪食物 -> 关闭抽油烟机 -> 洗碗吃饭
类图如下所示:
通过UML类图可以很明显地客户端不会直接依赖各个子系统,而是通过外观类间接地依赖。
详细代码如下:
【a】食物类:
public class Food {
private static volatile Food food;
private Food() {
}
public static Food getInstance() {
if (null == food) {
synchronized (Food.class) {
if (null == food) {
food = new Food();
}
}
}
return food;
}
public void buyFood() {
System.out.println("购买食物...");
}
public void washing() {
System.out.println("清洗食物...");
}
public void cooking() {
System.out.println("烹饪食物...");
}
}
【b】碗:
public class Bowl {
private static volatile Bowl bowl;
private Bowl() {
}
public static Bowl getInstance() {
if (null == bowl) {
synchronized (Bowl.class) {
if (null == bowl) {
bowl = new Bowl();
}
}
}
return bowl;
}
public void washing() {
System.out.println("洗碗...");
}
}
【c】油烟机
public class LampblackMachine {
private static volatile LampblackMachine lampblackMachine;
private LampblackMachine() {
}
public static LampblackMachine getInstance() {
if (null == lampblackMachine) {
synchronized (LampblackMachine.class) {
if (null == lampblackMachine) {
lampblackMachine = new LampblackMachine();
}
}
}
return lampblackMachine;
}
public void open() {
System.out.println("打开抽油烟机...");
}
public void close() {
System.out.println("关闭抽油烟机...");
}
}
【d】外观类:持有各个子系统的引用
public class CookingFacade {
/**
* 持有各个子对象的引用
*/
private Bowl bowl;
private Food food;
private LampblackMachine lampblackMachine;
public CookingFacade() {
this.bowl = Bowl.getInstance();
this.food = Food.getInstance();
this.lampblackMachine = LampblackMachine.getInstance();
}
/**
* 准备阶段: 买菜、洗菜、打开油烟机
*/
public void ready() {
food.buyFood();
food.washing();
lampblackMachine.open();
}
/**
* 烹饪阶段: 煮菜
*/
public void cooking() {
food.cooking();
}
/**
* 结束阶段: 关闭油烟机、洗碗
*/
public void end() {
lampblackMachine.close();
bowl.washing();
}
}
【e】客户端:客户端只需要与门面类打交道,不需要关注具体子系统如何实现。
public class Client {
public static void main(String[] args) {
CookingFacade facade = new CookingFacade();
//客户端简化了调用过程,通过调用外观高层接口直接依赖具体的各个子系统完成对应的功能
facade.ready();
facade.cooking();
facade.end();
}
}
运行结果如下:
可见,一个比较复杂的流程在客户端调用的时候简化了很多,如果是直接调用各个子系统的话,后期不好扩展。
四、外观模式应用分析
Mybatis中org.apache.ibatis.session.Configuration类的newMetaObject(Object object)就使用了外观模式。下面简要分析一下其中涉及外观模式的源码:
//Configuration.java
//Configuration持有一些子对象的引用,然后传入MetaObject类中构造方法
protected ReflectorFactory reflectorFactory;
protected ObjectFactory objectFactory;
protected ObjectWrapperFactory objectWrapperFactory;
//....
public Configuration() {
//....
this.reflectorFactory = new DefaultReflectorFactory();
this.objectFactory = new DefaultObjectFactory();
this.objectWrapperFactory = new DefaultObjectWrapperFactory();
//....
}
//这里实际上就是通过各个子系统去创建MetaObject对象,对外提供了一个高层简化接口,解耦客户端和各个子系统
public MetaObject newMetaObject(Object object) {
//调用MetaObject的静态方法forObject
return MetaObject.forObject(object, this.objectFactory, this.objectWrapperFactory, this.reflectorFactory);
}
//MetaObject.java
//forObject方法
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
//调用构造方法
return object == null ? SystemMetaObject.NULL_META_OBJECT : new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
//构造方法
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper)object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map)object);
} else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection)object);
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
角色分析:
- 子系统对象:this.objectFactory, this.objectWrapperFactory, this.reflectorFactory,具体完成某项功能的类;
- 外观对象:Configuration类,提供高层接口的类;
五、总结
外观模式的优点:
- 对外隐藏了各个子系统的内部实现细节,降低了客户端对各个子系统的使用复杂度;
- 解耦了客户端和子系统,让各个子系统的功能更容易维护和扩展;
- 减少系统的相互依赖,所有的依赖都是对门面对象的依赖,与子系统无关;
外观模式的缺点:
- 不能很好地限制客户端使用子系统类,即客户端还是可以直接调用各个子系统相关方法;
- 增加新的子系统可能需要修改外观类或客户端的源代码,违反“开闭原则”;
外观模式的适用场景:
- 当需要维护一个已经很难扩展和维护的旧系统时,可以考虑为新系统提供一个高层接口,用于与旧系统的对接,方便接口调用;
- 当系统需要分层设计时,可以考虑使用外观模式;
- 当需要简化一个很大的接口或者一组复杂的接口时,可以考虑使用外观模式;
- 为一个复杂的模块或子系统提供一个供外界访问的接口;
下一篇: Facade(外观模式) 结构型