以家庭影院引入外观者模式
组建一个家庭影院:
DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为:
- 直接用遥控器:统筹各设备开关
- 开爆米花机
- 放下屏幕
- 开投影仪
- 开音响
- 开DVD,选dvd
- 去拿爆米花
- 调暗灯光
- 播放
- 观影结束后,关闭各种设备
传统思路
直接让客户端依赖(使用) 所有的设备对象,这样的设计会使得客户端比较混乱,一时间难以兼顾维护多个设备。

问题分析
在 Client 的 main 方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程
不利于在 Client 中,去维护对子系统的操作。
解决思路
给设备和客户端之间 “加一层”,让中间这一层去管理设备,客户端只要和中间这层对接即可。
外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节。
例如:
外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用
原理类图

三个子系统
- //DVD播放器
- public class DVDPlayer {
- private static DVDPlayer instance = new DVDPlayer();
-
- public static DVDPlayer getInstance(){
- return instance;
- }
-
- //DVD的操作方法;
- public void open(){
- System.out.println("DVD开机");
- }
-
- public void close(){
- System.out.println("DVD关机");
- }
-
- public void pause(){
- System.out.println("DVD暂停");
- }
-
- public void option(){
- System.out.println("DVD使用操作");
- }
- }
- //灯光
- public class Lamplight {
- private static Lamplight instance = new Lamplight();
-
- public static Lamplight getInstance(){
- return instance;
- }
-
- //管理灯光;
- public void openLamp(){
- System.out.println("开灯");
- }
-
- public void closeLamp(){
- System.out.println("关灯");
- }
- }
- //爆米花设备
- public class PopCorn {
- private static PopCorn instance = new PopCorn();
-
- public static PopCorn getInstance(){
- return instance;
- }
- //爆米花设备操作;
- public void openPop(){
- System.out.println("爆米花设备开机");
- }
-
- public void closePop(){
- System.out.println("爆米花设备关机");
- }
-
- public void pop(){
- System.out.println("取爆米花");
- }
- }
外观类
- //外观类
- public class HomeTheaterFacade {
- // 管理多个设备
- private DVDPlayer dvdPlayer;
- private PopCorn popCorn;
- private Lamplight screen;
-
- public HomeTheaterFacade(){
- dvdPlayer = DVDPlayer.getInstance();
- popCorn = PopCorn.getInstance();
- screen = Lamplight.getInstance();
- }
- //观影前准备
- public void ready(){
- popCorn.openPop();
- popCorn.pop();
- screen.openLamp();
- }
- //观影中管理
- public void play(){
- dvdPlayer.open();
- }
- //暂停观影
- public void pause(){
- dvdPlayer.pause();
- }
- //结束观影
- public void end(){
- dvdPlayer.close();
- popCorn.closePop();
- screen.closeLamp();
- }
- }
使用测试
- public class Client {
- public static void main(String[] args) {
- HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
- homeTheaterFacade.ready();
- homeTheaterFacade.play();
- homeTheaterFacade.pause();
- homeTheaterFacade.end();
- }
- }
结果
爆米花设备开机
取爆米花
开灯
DVD开机
DVD暂停
DVD关机
爆米花设备关机
关灯
用户只需要调用Configuration的newMetaObject(Object object)方法就可以得到一个MetaObject对象,而具体的对象是怎么生成与用户无关,下面我们可以看一下Configuration的部分源码分析。
- public class Configuration {
-
- public MetaObject newMetaObject(Object object) {
- return MetaObject.forObject(object, this.objectFactory, this.objectWrapperFactory, this.reflectorFactory);
- }
-
- //......
-
- }
下面看看Configuration的构造器,它里面创建了这三个实例。
- public Configuration() {
-
- this.reflectorFactory = new DefaultReflectorFactory();
- this.objectFactory = new DefaultObjectFactory();
- this.objectWrapperFactory = new DefaultObjectWrapperFactory();
-
- //......
-
- }
进入MetaObject类的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);
- }
它里面聚合了objectFactory,objectWrapperFactory,reflectorFactory
objectFactory,objectWrapperFactory,reflectorFactory可以看作三个子系统
- public class MetaObject {
- private final Object originalObject;
- private final ObjectWrapper objectWrapper;
- private final ObjectFactory objectFactory;
- private final ObjectWrapperFactory objectWrapperFactory;
- private final ReflectorFactory 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);
- }
-
- }
-
- //......
- }

1)外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
2)外观模式让客户端与子系统解耦,让子系统内部的模块更易维护和扩展
3)通过合理的使用外观模式,可以帮我们更好的划分访问的层次
4)当系统需要进行分层设计时,可以考虑使用 Facade 模式
5)在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个 Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade 类交互,提高复用性
6)不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。