前言
- 通过一个影院管理项目来了解外观模式
影院管理项目
组建一个家庭影院 :
DVD 播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为: 直接用遥控器:统筹各设备开关
开爆米花机
放下屏幕
开投影仪
开音响
开 DVD,选 dvd
去拿爆米花
调暗灯光
播放
观影结束后,关闭各种设备
传统方式解决影院管理
传统方式解决影院管理问题分析
1)在ClientTest的main方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程。
2)不利于在ClientTest中,去维护对子系统的操作。
3)解决思路 :定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法ready,play,pause,end),用来访问子系统中的一群接口
4)也就是说,通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关系这个系统的内部细节 => 外观模式
外观模式基本介绍
1)外观模式(Facade),也叫“过程模式”:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义类一个高层接口,这个接口使得这一子系统更加容易使用
2)外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节
外观模式原理类图
类图说明 :
1)外观类(Facade):为调用端提供统一的调用接口,外观类知道那些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象
2)调用者(Client):外观接口的调用者
3)子系统的集合 :指模块或者子系统,处理Facade对象指派的任务,他是功能的实际提供者。
外观模式解决影院管理
1)外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口才能达到目的。比如 :在pc上安装软件的时候经常有一键安装选项(等等),还有手机重启功能(把关机和启动合为一个操作)。
2)外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用。
具体实现
package java设计模式.Facade; public class DVDPlayer { //使用单例模式, 使用饿汉式 //类的内部创建对象 private final static DVDPlayer instance=new DVDPlayer(); private DVDPlayer(){ } //向外提供一个静态公共方法 public static DVDPlayer getInstance(){ return instance; } //开启 public void on() { System.out.println("DVD开始运行"); } //关闭 public void off() { System.out.println("DVD关闭"); } //运行 public void play() { System.out.println("DVD正在运行"); } //DVD放完了 public void pause() { System.out.println("DVD放完了"); } }
package java设计模式.Facade; //爆米花机 public class Popcorn { private Popcorn(){ } private final static Popcorn instance=new Popcorn(); public static Popcorn getInstance(){ return instance; } //爆米花机开机 public void on() { System.out.println("爆米花机开机"); } //爆米花机关机 public void off() { System.out.println("爆米花机关机"); } //爆米花机正在制作爆米花 public void pop() { System.out.println("爆米花机正在制作爆米花"); } }
package java设计模式.Facade; public class Projector { //创建投影机 private static Projector instance = new Projector(); public static Projector getInstance() { return instance; } //投影机开机 public void on() { System.out.println("投影机开机"); } //投影机关机 public void off() { System.out.println("投影机关机"); } //投影机正在运行 public void focus() { System.out.println("投影机正在运行"); } }
package java设计模式.Facade; public class Screen { //创建荧幕 private static Screen instance = new Screen(); public static Screen getInstance() { return instance; } //上升荧幕 public void up() { System.out.println("上升荧幕"); } //下降荧幕 public void down() { System.out.println("下降荧幕"); } }
package java设计模式.Facade; public class Stereo { //创建立体声机 private static Stereo instance = new Stereo(); public static Stereo getInstance() { return instance; } //立体声机开机 public void on() { System.out.println("立体声机开机"); } //立体声机关机 public void off() { System.out.println("立体声机关机"); } //立体声机正在运行 public void up() { System.out.println("立体声机正在运行"); } }
package java设计模式.Facade; //灯光 public class TheaterLight { //创建灯光 private static TheaterLight instance = new TheaterLight(); public static TheaterLight getInstance() { return instance; } //灯光开启 public void on() { System.out.println("灯光开启"); } //灯光关闭 public void off() { System.out.println("灯光关闭"); } //灯光调暗 public void dim() { System.out.println("灯光调暗"); } //灯光调亮 public void bright() { System.out.println("灯光调亮"); } }
package java设计模式.Facade; //外观类 public class HomeTheaterFacade { //定义各个子系统对象 private TheaterLight theaterLight; private Popcorn popcorn; private Stereo stereo; private Projector projector; private Screen screen; private DVDPlayer dVDPlayer; //构造器 public HomeTheaterFacade() { // super(); this.theaterLight = TheaterLight.getInstance(); this.popcorn = Popcorn.getInstance(); this.stereo = Stereo.getInstance(); this.projector = Projector.getInstance(); this.screen = Screen.getInstance(); this.dVDPlayer = DVDPlayer.getInstance(); } //操作分成 4 步 //电影院准备 public void ready() { popcorn.on(); popcorn.pop(); screen.down(); projector.on(); stereo.on(); dVDPlayer.on(); theaterLight.dim(); } //电影开始 public void play() { dVDPlayer.play(); } //电影结束 public void pause() { dVDPlayer.pause(); } //电影院关闭 public void end() { popcorn.off(); theaterLight.bright(); screen.up(); projector.off(); stereo.off(); dVDPlayer.off(); } }
package java设计模式.Facade; public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //这里直接调用。。 很麻烦 HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(); //电影院准备 homeTheaterFacade.ready(); //电影院开始 homeTheaterFacade.play(); //电影院结束 homeTheaterFacade.end(); } }
Configuration源码结构分析
1、类图分析
下面我们看看Mybatis的源码来具体理解外观模式。
public MetaObject newMetaObject(Object object) { return MetaObject.forObject(object, this.objectFactory, this.objectWrapperFactory, this.reflectorFactory); }
上述代码其实是完成一个创建MetaObject的事情,但是它是将一个负责创建MetaObject的子系统放在了这个方法里面。为什么要这么做?实际上如果直接让我们应用层去使用MetaObject.forObject(object, this.objectFactory, this.objectWrapperFactory, this.reflectorFactory);这个方法。可以看出参数实在太多,而Configuration类使用外观模式,外观类并不具体实现什么,他只是负责调用和管理子系统
下面看看configuration中的构造器 可以把上面的objectFactory,objectWrapperFactory,reflectorFactory看作三个子系统
接下来到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); }
对应的构造函数
可以看出这个MetaObject也是个将构造器私有的特殊单例模式
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); } }
总结
外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展
通过合理的使用外观模式,可以帮我们更好的划分访问的层次
当系统需要进行分层设计时可以考虑使用外观模 式
在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个外观类,来提供遗留系统的比较清晰简单的接口,让新系统与外观类交互, 提高复用性
不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好,要以让系统有层次,利于维护为目的 。