设计模式之外观模式
外观模式介绍:外观模式(Facade),也叫"过程模式", 外观模式为子系统中的一组接口提供一个一致的界面,此模式定了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式通过定义一个一致的接口,用以屏蔽内部子雄他那个的细节,使得调用端只需要跟这个接口发生调用,而不需关心这个子系统的内部细节。
外观模式的原理类图如下:
有一个外观类,里面聚合了各个子系统,然后Client客户端依赖使用这个外观类。
外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求。从而将调用端的请求代理给子系统对象。
调用者类(Client) :外观接口调用者
子系统的集合:指模块或者子系统,处理Facade对象指派的一个任务,他是功能的实际提供者。
创建一个家庭影院的项目,DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求用java完成家庭影院的功能。
使用过程:
1、直接用遥控器,统筹各种设备开关
2、开爆米花机
3、放下屏幕
4、开投影仪
5、升音响
6、开DVD, 选dvd
7、去拿爆米花
8、调暗灯光
9、播放
10、观影结束关闭各种设备
如果用传统方式解决的话,大家容易想到的是,创建出一系列的遥控器类,每个遥控器类控制不同的功能,但是这样势必会造成遥控器类非常多。如下
那么以上解决方案的主要问题如下:
1、子系统对象非常多,操作步骤多,会造成调用过程混乱,没有清晰的过程。
2、不利于在ClientTest中去维护子系统的操作。如果各个子系统需要修改,造成ClientTest也需要跟着修改。
解决思路:
定义一个高层接口,给子系统的一组接口提供一个一致的界面(比如在高层接口提供ready,play,pause,end),用来访问子系统中的一群接口。也就是定义一个一致的接口(界面类),用以屏蔽子系统的细节,使得调用端只跟整个接口发生调用,而无需关心这个子系统的内部细节。=》引入外观模式。
代码实现:
TheaterLight.java
public class TheaterLight { private static TheaterLight theaterLight = new TheaterLight(); public static TheaterLight getInstance(){ return theaterLight; } public void on(){ System.out.println("theaterLight on"); } public void off(){ System.out.println("theaterLight off"); } public void dim(){ System.out.println("theaterLight dim"); } public void bright(){ System.out.println("theaterLight bright"); } }
Stereo.java
public class Stereo { private static Stereo stereo = new Stereo(); public static Stereo getInstance(){ return stereo; } 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 void down(){ System.out.println("Stereo down"); } }
Screen.java
public class Screen { private static Screen screen = new Screen(); public static Screen getInstance(){ return screen; } public void up(){ System.out.println("Screen up"); } public void down(){ System.out.println("Screen down"); } }
Projector.java
public class Projector { private static Projector projector = new Projector(); public static Projector getInstance(){ return projector; } 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"); } }
Popcorn.java
public class Popcorn { private static Popcorn popcorn = new Popcorn(); public static Popcorn getInstance(){ return popcorn; } 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"); } }
DVDPlayer.java
public class DVDPlayer { private static DVDPlayer instance = new DVDPlayer(); public static DVDPlayer getInstance(){ 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 is pausing"); } }
HomeTheaterFacade.java
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(); //dvd打开 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(); //关闭dvd dvdPlayer.off(); } }
Client.java
public class Client { public static void main(String[] args) { HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(); homeTheaterFacade.ready(); homeTheaterFacade.play(); homeTheaterFacade.pause(); homeTheaterFacade.end(); } }
外观模式的注意事项和细节
1)外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性。
2)外观模式对客户端与子系统的耦合关系进行解耦,使子系统内部的模块更易于维护和扩展。
3)通过外观模式,可以帮助我们更好的划分访问的层次。
4)当系统需要分层设计时可以考虑Facade模式。
5)在维护一个遗留的大系统时,可能这个系统已经十分的难以维护和拓展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性。
6)不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好,要以让系统有层次,利于维护为目的。