外观设计模式
1、影院管理需求
【1】需求
组件一个家庭影院:
DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为:
- 直接用遥控器,统筹各设备开关
- 开爆米花机
- 放下屏幕
- 开投影仪
- 开音响
- 开DVD,选DVD
- 去拿爆米花
- 调暗灯光
- 播放
- 观影结束后,关闭各种设备
【2】传统方式解决影院管理需求存在的问题分析
1)在ClientTest的main方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程。
2)不利于在ClientTest中去维护对子系统的操作。
3)解决思路:定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法ready、play、pause、end),用来访问子系统中的一群接口。
4)也就是说就是通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节=>外观模式
2、外观模式基本介绍
1)外观模式(Facade),也叫“过程模式”:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
2)外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节。
3)外观模式是”迪米特法则”的典型应用。
3、外观模式的结构
外观(Facade)模式包含以下主要角色:
- 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
- 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
4、外观模式解决影院管理问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
| public class DVDPlayer { private static volatile DVDPlayer instance; private DVDPlayer() {} public static DVDPlayer getInstance() { if (null == instance) { synchronized(DVDPlayer.class) { if (null == instance) { instance = new DVDPlayer(); } } } return instance; } public void on() { System.out.println(" dvd on "); } public void off() { System.out.println(" dvd off "); } public void play() { System.out.println(" dvd is playing "); } public void pause() { System.out.println(" dvd pause.. "); } }
public class Popcorn { private static volatile Popcorn instance; private Popcorn() {} public static Popcorn getInstance() { if (null == instance) { synchronized(Popcorn.class) { if (null == instance) { instance = new Popcorn(); } } } return instance; } public void on() { System.out.println(" popcorn on "); } public void off() { System.out.println(" popcorn off "); } public void pop() { System.out.println(" popcorn is poping "); } }
public class Projector { private static volatile Projector instance; private Projector() {} public static Projector getInstance() { if (null == instance) { synchronized(Projector.class) { if (null == instance) { instance = new Projector(); } } } return instance; } public void on() { System.out.println(" projector on "); } public void off() { System.out.println(" projector off "); } public void focus() { System.out.println(" projector is focusing "); } }
public class Screen { private static volatile Screen instance; private Screen() {} public static Screen getInstance() { if (null == instance) { synchronized(Screen.class) { if (null == instance) { instance = new Screen(); } } } return instance; } public void up() { System.out.println(" screen up "); } public void down() { System.out.println(" screen down "); } }
public class Stereo { private static volatile Stereo instance; private Stereo() {} public static Stereo getInstance() { if (null == instance) { synchronized(Stereo.class) { if (null == instance) { instance = new Stereo(); } } } return instance; } public void on() { System.out.println(" stereo on "); } public void off() { System.out.println(" stereo off "); } public void up() { System.out.println(" stereo up.. "); } }
public class TheaterLight { private static volatile TheaterLight instance; private TheaterLight() {} public static TheaterLight getInstance() { if (null == instance) { synchronized(TheaterLight.class) { if (null == instance) { instance = new TheaterLight(); } } } return instance; } public void on() { System.out.println(" TheaterLight on "); } public void off() { System.out.println(" TheaterLight off "); } public void bright() { System.out.println(" TheaterLight bright.. "); } }
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(); } public void ready() { popcorn.on(); popcorn.pop(); screen.down(); projector.on(); stereo.on(); dVDPlayer.on(); theaterLight.off(); } public void play() { dVDPlayer.play(); } public void pause() { dVDPlayer.pause(); } public void end() { popcorn.off(); theaterLight.on(); screen.up(); projector.off(); stereo.off(); dVDPlayer.off(); } }
public class Client { public static void main(String[] args) { HomeTheaterFacade h = new HomeTheaterFacade(); h.ready(); h.play(); } }
|
5、外观模式的优缺点及使用场景
【1】好处
- 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
- 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
【2】缺点
【3】使用场景
- 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
- 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
- 当客户端与多个子系统之间存在很大的联系时,引入外观模式可以将它们分离,从而提高子系统的独立性和可移植性。
6、外观模式源码解析
【1】外观模式在MyBatis框架应用分析
1)MyBatis中的Configuration去创建MetaObject对象使用到外观模式。
2)代码分析+Debug源码
【2】外观模式在tomcat中的解析
使用tomcat作为web容器时,接收浏览器发送过来的请求,tomcat会将请求信息封装成ServletRequest对象,如下图①对象,但是大家想想ServletRequest是一个接口,它还有一个子接口HttpServletRequest,而我们知道该request对象肯定是一个HttpServletRequst对象的子实现类对象,到底是哪个类的对象呢?可以通过输出request对象,我们就会发现是一个名为RequestFacade类的对象。
1 2 3 4 5 6 7 8
| public class ServletDemo extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) { } protected void doGet(HttpServletRequest request, HttpServletResponse response) { this.doPost(request, response); } }
|
RequestFacade类就使用了外观模式。先看结构图:

为什么在此处使用外观模式呢?
定义RequestFacade类,分别实现ServletRequest,同时定义受保护的成员变量Request,并且方法的实现调用Request的实现。然后,将RequestFacade上转为ServletRequest传给servlet的service方法,这样即使在servlet中被下转为RequestFacade,也不能访问受保护的成员变量对象中的方法。既用了Request,又能防止其中方法被不合理的访问。
7、外观模式的注意事项和细节
1)外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性。
2)外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展。
3)通过合理的使用外观模式,可以帮我们更好的划分访问的层次。
4)当系统需要进行分层设计时,可以考虑使用Facade模式。
5)在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性。
6)不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。