外观模式
模式定义
提供一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统的使用变得更加容易(满足大部分需要),同时不限制对子系统更加精确的控制。GOF95把外观模式分类为结构型模式。
模式结构与说明
- Facade角色:Client可以调用Facade的方法。Facade知晓一个或者多个子系统的功能与职责。通常情况下,Client的调用被本角色委派到相应子系统中去
- Subsystem角色:可以包含一个或者多个子系统,每个子系统可以是多个类的集合,每个子系统都可以被Client独立调用,也可以被Facade调用
外观模式提供一个更合理接口的外观类,简化复杂子系统的使用,很好的遵循了迪米特法则。它具有以下优点:
- 没有对原有接口进行封装,因此Client仍然可以使用那些接口
- 允许Client从子系统中解耦
- 可以添加“更聪明”的功能,该功能通常调用子系统多个接口,简化使用
外观模式的缺点是:
- 过多或者不适当的Facade容易造成Client的迷惑,Client不知道该直接调用Subsystem还是Facade
以下情况下适用外观模式:
- 需要为一个复杂的子系统提供一个简单接口时
- 需要提高子系统的独立性时:为子系统引入Facade可以将其与客户端、其它子系统分离
- 需要层次化结构时:某些分层架构设计中,会引入一个Facade层,为Service层提供一个简易的接口,简化层层之间的依赖
应用举例
考虑建设一个家庭影院,我们需要投影仪、幕布、DVD播放器、CD播放器、功放音响、影院灯光甚至冰淇淋机等设备:
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 |
# -*- coding: UTF-8 -*- # CD播放机 class CDPlayer: def on( self ):pass def off( self ):pass def eject( self ):pass def pause( self ):pass def play( self ):pass def stop( self ):pass # DVD播放机 class DVDPlayer: def on( self ):pass def off( self ):pass def eject( self ):pass def pause( self ):pass def play( self ):pass def stop( self ):pass # 功放,使用CD或者DVD播放器作为输入,可以调整其音量 class Amplifier: @property def dvdPlayer( self ): return self.dvdPlayer @property def cdPlayer( self ): return self.cdPlayer @dvdPlayer.setter def dvdPlayer( self, dvdPlayer ): self.dvdPlayer = dvdPlayer @cdPlayer.setter def cdPlayer( self, cdPlayer ): self.cdPlayer = cdPlayer def on( self ):pass def off( self ):pass def setInputMode( self , inputMode ):pass def setVolume( self ):pass def setSurroundAudio( self ):pass # 投影仪,以DVD播放器为输入 class Projector( object ): @property def dvdPlayer( self ): return self.dvdPlayer @dvdPlayer.setter def dvdPlayer( self, dvdPlayer ): self.dvdPlayer = dvdPlayer def on( self ):pass def off( self ):pass def tvMode( self ): pass def wideScrMode( self ):pass # 幕布 class Screen: def up( self ): pass def down( self ):pass # 灯具 class Light: def on( self ): pass def off( self ):pass def dim( self ):pass # 冰淇淋机 class IcecreamMaker: def on( self ):pass def off( self ):pass def make( self ):pass |
好了,怎么样来看一场电影呢?一个麻烦的方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
if __name__ == '__main__': icecreamMaker = IcecreamMaker() dvdPlayer = DVDPlayer() cdPlayer = CDPlayer() amplifier = Amplifier() amplifier.cdPlayer = cdPlayer amplifier.dvdPlayer = dvdPlayer projector = Projector() projector.dvdPlayer = dvdPlayer screen = Screen() light = Light() icecreamMaker.on() # 打开冰淇淋机 icecreamMaker.make() # 制作冰淇淋 screen.down() # 降下幕布 projector.on() # 打开投影仪 projector.wideScrMode() # 切换为宽屏模式 amplifier.on() # 打开功放 amplifier.setInputMode( 1 ) # 设置输入为DVD amplifier.setSurroundAudio() # 设置为环绕立体声 amplifier.setVolume( 85 ) # 设置音量为85 dvdPlayer.on() # 打开DVD播放器 dvdPlayer.play() # 开始播放 |
真够折腾的啊,看一个电影要10多个操作步骤,还好我不是专职的放映员
看完电影呢,还需要把相反的动作依次执行一遍,如果要听CD或者广播,操作步骤还会不一样,以后要是升级系统引入新设备,就更是一团糟了。
怎么样才能简化操作呢?外观模式能很好的解决问题,我们引入一个智能遥控器(外观):
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 |
# 智能遥控器 class HomeTheaterFacade: def __init__( self ): self.icecreamMaker = IcecreamMaker() self.dvdPlayer = DVDPlayer() self.cdPlayer = CDPlayer() self.projector = Projector() self.amplifier = Amplifier() self.screen = Screen() self.light = Light() self.amplifier.cdPlayer = self.cdPlayer self.amplifier.dvdPlayer = self.dvdPlayer self.projector.dvdPlayer = self.dvdPlayer def watchMovie( self ): self.icecreamMaker.on() self.icecreamMaker.make() self.screen.down() self.projector.on() self.projector.wideScrMode() self.amplifier.on() self.amplifier.setInputMode( 1 ) self.amplifier.setVolume( 85 ) self.dvdPlayer.on() self.dvdPlayer.play() def endMovie( self ):pass def listenToCd( self ):pass def endCd( self ):pass |
现在,作为Client的你,只需要舒服的躺在沙发上,按一下按钮就可以看电影了,是不是很爽呢?
1 2 3 |
if __name__ == '__main__': facade = HomeTheaterFacade() facade.watchMovie() |
经典应用
JFace中的外观模式
JavaServer Faces (JSF) 是一种用于构建Java Web应用程序的标准框架,它提供了一种以组件为中心的用户界面构建方法。该框架中大量使用了外观模式:
FacesContext作为Lifecycle、ViewHandler、NavigationHandler以及很多Client不需要关心的类的外观,简化了Client编程复杂度。
编译器
我们使用gcc时,只需要简单的命令就可以编译出可执行文件,实质上,gcc在内部经历了预处理、编译、链接等几个阶段,牵涉到数十个组件的协作,这种简化的命令是外观模式的体现。
模式演变
- 外观模式与适配了多个Target的适配器有些类似,但是后者必须提供所有Target的接口,外观模式则不需要。两者的意图也不一样
- 与中介者模式的比较:中介者模式主要用来封装多个对象之间的相互调用,常用在系统的多个模块之间;外观模式主要封装单向的调用,常用在客户端与系统之间。中介者模式一般需要实现具体的交互功能;外观模式只是组合调用或者转调,一般不实现实质性的业务逻辑
- 通常一个子系统只需要单个Facade实例,因此Facade通常使用单例模式来实现
Leave a Reply