设计模式 -- 外观模式
1、什么是外观模式?
外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。简单来说外观模式就是在简化接口。简化谁的接口呢?一般的是简化我们已有子系统的接口,比如我们的家庭影院遥控器上的按钮,当我们按下“播放”这个按钮时实际上发生了很多事:调整灯光亮度,落下屏幕,开启投影,调整投影屏幕模式……对于这些繁琐的操作,我们不用关心到底是谁调用了灯光或是落下了屏幕,我们只需要按下“播放”即可。“播放”就是一个被简化了的接口,供我们使用,以避免我们在想看电影时做很多繁琐的操作。如此说来外观模式就非常简单了,我们需要定义自己的类,在其中维护需要简化接口的子系统,然后写一个“外观接口”就OK了!
2、如何实现外观模式?
正如上面所说的,实现外观,需要我们知道“有哪些子系统需要简化接口”、“编写简化接口函数”,基本设计类图如下:
Facade就是我们要编写的外观类,这个类只需要提供接口,供用户使用,而真正实现这些接口时,必须调用相应的子系统来工作,也就是说,我们应该使用组合能让外观访问到子系统中所有的组件。
SubSystem1和SubSystem2就是我们的子系统,这些子系统只管自己要做的工作,至于他们的工作时如何组织成一个完整的简单的可供用户使用的功能,是由外观来完成的。
3、举例分析
我们仍以“家庭影院”这个例子说明,当用户按下“播放按钮”时,会发生一系列的事情,而这一系列的事都是由外观组织起来的。
看看设计类图:
看到这个图,你也许有一点摸不着头脑了,因为这里面没有看不到类间的关系。其实这个关系我们在前面已经说过了,就是一种简单的“组合”,HomeTheaterFacade这个类就是我们的外观类,他需要“知道”所有的子系统(因为他的每一项任务都将委托子系统中相应组件来处理),这种“知道”我们用组合来实现,即高层外观类需要维护所有的子系统类的对象(这也是外观模式的一个缺点)。
具体代码:
namespace 外观模式____家庭影院 { public class HomeTheaterFacade { //高层外观需要维护所有子系统类的对象 Amplifier amp; Tuner tuner; DvdPlayer dvd; CdPlayer cd; Projector pro; Screen screen; TheaterLights lights; PopcornPopper popper; public HomeTheaterFacade(Amplifier amp, Tuner tuner, DvdPlayer dvd, CdPlayer cd, Projector pro, Screen screen, TheaterLights lights, PopcornPopper popper) { this.amp = amp; this.tuner = tuner; this.dvd = dvd; this.cd = cd; this.screen = screen; this.lights = lights; this.popper = popper; this.pro = pro; } //外观暴露给用户的简化接口 -- 看电影 public void watchMove(string movie) { Console.WriteLine("Get ready to watch a movie……"); popper.on(); popper.pop(); lights.dim(10); screen.down(); pro.on(); pro.wideScreenMode(); DvdPlayer dvd = new DvdPlayer(); amp.on(); amp.setDVD(dvd); amp.setSurrondedSound(); amp.setVolume(5); dvd.on(); dvd.play(movie); } //外观暴露给用户的简化接口 -- 关闭电影 public void endMovie() { Console.WriteLine("Shutting movie theater down……"); popper.off(); lights.on(); screen.up(); pro.off(); amp.off(); dvd.stop(); dvd.eject(); dvd.off(); } } }
//子系统 - 扩音器 public class Amplifier { DvdPlayer dvd; public void on() { Console.WriteLine("Top - O - Line Amplifier is on\n"); } public void off() { Console.WriteLine("Top - O - Line Amplifier is off\n"); } public void setDVD(DvdPlayer dvd) { this.dvd = dvd; Console.WriteLine("Top - O - Line Amplifier setting DVD player to TOP - O -Line DVD player"); } public void setSurrondedSound() { Console.WriteLine("Top - O - Line Amplifier surround sound on (5 speakers, 1 subwoofer)"); } public void setVolume(int num) { Console.WriteLine("Top - O - Line Amplifier setting volume to %d\n",num); } // public string toString() // { } }
//子系统 - 大屏幕 public class Screen { public void down() { Console.WriteLine("Theater Screen going down"); } public void up() { Console.WriteLine("Theater Screen going up"); } } }
//子系统 - 投影仪 public class Projector { public void on() { Console.WriteLine("Top - O - Line Projector on"); } public void wideScreenMode() { Console.WriteLine("Top - O - Line Projector is in widescreen mode(16*9 aspect ratio)"); } public void off() { Console.WriteLine("Top - O - Line Projector off"); } } }
//子系统 - DVD播放器 public class DvdPlayer { string movie; public void play(string movie) { this.movie = movie; Console.WriteLine("Top - O - Line DVD player is playing "+this.movie); } public void on() { Console.WriteLine("Top - O - Line DVD player on"); } public void stop() { Console.WriteLine("Top - O - Line DVD player stopped "+this.movie); } public void eject() { Console.WriteLine("Top - O - Line DVD player eject"); } public void off() { Console.WriteLine("Top - O - Line DVD player off"); } }
其他的子系统方法省略……自己写写啦,很简单的~
外观模式很好的体现了一个设计原则——“最少知识原则”,最少知识原则就是说在设计一个系统时,不管是任何对象,你都应该注意他所交互的类,并注意他和这些类是如何交互的,我们注意这些目的是不要让太多的类耦合在一起,免得修改系统中的一部分会影响到其他的部分。在Facade这个模式中,Client只有一个“朋友”——Facade,这个“朋友”帮助Client管理了所有的子系统部件,而Client并不知道各个子系统部件在做着什么工作,也就是说Client与子系统解耦了~