1.前言
代理和装饰模式都是对单个对象的封装,不熟悉的朋友可以到设计模式文集中查看一下。若将这个对象扩展一下,让它成为具有某种业务功能的模块,甚至系统,会如何?对,说到底,它仍是一个对象。不过这个对象在工作时,内部的子对象会相互调用,完成数据的传递,共同支持着整个大的对象。
2.概念
外观模式要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。目的很简单,就是为了对外隐藏子系统的具体实现、隔离变化。
因为子系统的功能可能一开始就比较复杂,又或者随着使用而变得复杂。一旦复杂,根据单一职责原则,将拆分出更多、更小的类来负责其中的一个方面。对于使用者而言,必须了解到每个类的作用才能灵活使用,这无疑增加了学习的成本。所以直接提供子系统功能的调用,拒绝对其内部的访问。
若系统具有层次结构,那么每一层都相当于是子系统,并且之间相互依赖。当某一层需要改变或替换时,将会影响到相关的层级,这无疑增加了维护的难度。可在每层的入口提供调用的抽象,将依赖关系进行隔离。
3.场景
现在的汽车是个很复杂的系统,由不少小的子系统组成,其中动力系统包含发动机,运行系统包含轮胎。但作为司机,若想开动汽车,不需要知道如何启动发动机,再将动力传输给轮胎,这一切相应的系统内部会处理。若想更换不同类型的轮胎,也不需要改造汽车,它们之间不是直接相连,而是通过标准的轴承相连。
4.写法
子系统内部各层级间调用的抽象,以及对应的具体实现。
public interface Engine {
void startUp();
}
public class Benz implements Engine {
@Override
public void startUp() {
System.out.println("发动奔驰引擎");
}
}
public interface Tyre {
void roll();
}
public class Giti implements Tyre {
@Override
public void roll() {
System.out.println("使用佳通轮胎");
}
}
public class Michelin implements Tyre {
@Override
public void roll() {
System.out.println("使用米其林轮胎");
}
}
任何一个系统相对于使用者所在的系统,也是个子系统,它与内部系统的关系是具有层级的。通过抽象的接口调用,可以增加维护的灵活性,减少对自己的影响。
// 1.声明(子)系统对外的对象
public class Car {
// 2.列出包含的子系统
private Engine mEngine;
private Tyre mTyre;
public Car(Engine engine, Tyre tyre) {
mEngine = engine;
mTyre = tyre;
}
// 3.设置子系统的变化
public void setmTyre(Tyre tyre) {
mTyre = tyre;
}
// 4.对外展示功能的调用
public void drive() {
startUp();
roll();
}
// 5.内部具体实现
private void startUp() {
mEngine.startUp();
}
private void roll() {
mTyre.roll();
}
}
提供一个统一的高层接口,使功能的调用简单明确,降低用户使用成本,也对用户屏蔽许多不需要知道的细节。
public class Client {
public static void main(String[] args) {
// 1.创建子系统实例
Car mCar = new Car(new Benz(), new Giti());
// 2.调用对外功能接口
mCar.drive();
// 3.子系统内部改变
mCar.setmTyre(new Michelin());
mCar.drive();
}
}
5.总结
外观模式就是最常用的封装,将完整的、复杂的功能作为一个对象封装起来。由于用户不知道细节,降低了使用的耦合度;又将复杂的功能逻辑放在内部,降低了使用的难度。可能的缺点便是,通过接口降低内部逻辑的耦合,便于维护的同时,增加了过多的代码。