简介
外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。
这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
外观模式包含如下两个角色:
- Facade(外观角色):在客户端可以调用它的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理。
- SubSystem(子系统角色):在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。
类图
代码
/**
* SubSystem(子系统角色):音响
* @author: 张彬
* @date: 2018年5月29日 下午3:20:53
* @version: V1.0
* @review: 张彬/2018年5月29日 下午3:20:53
*/
public class Audio {
private static Audio instance = new Audio();
private Audio(){}
public static Audio getInstance(){
return instance;
}
public void on(){
System.out.println("音响打开!!!");
}
public void off(){
System.out.println("音响关闭!!!");
}
}
/**
* SubSystem(子系统角色):DVD播放器
* @author: 张彬
* @date: 2018年5月29日 下午3:14:21
* @version: V1.0
* @review: 张彬/2018年5月29日 下午3:14:21
*/
public class DvdPlayer {
private 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关闭!!!");
}
}
/**
* SubSystem(子系统角色):灯光
* @author: 张彬
* @date: 2018年5月29日 下午3:13:22
* @version: V1.0
* @review: 张彬/2018年5月29日 下午3:13:22
*/
public class Light {
private static Light instance = new Light();
private Light(){}
public static Light getInstance(){
return instance;
}
public void on(){
System.out.println("灯光打开!!!");
}
public void off(){
System.out.println("灯光关闭!!!");
}
}
/**
* SubSystem(子系统角色):投影仪
* @author: 张彬
* @date: 2018年5月29日 下午3:15:30
* @version: V1.0
* @review: 张彬/2018年5月29日 下午3:15:30
*/
public class Projector {
private static Projector instance = new Projector();
private Projector(){}
public static Projector getInstance(){
return instance;
}
public void on(){
System.out.println("投影仪打开!!!");
}
public void off(){
System.out.println("投影仪关闭!!!");
}
}
/**
* SubSystem(子系统角色):屏幕
* @author: 张彬
* @date: 2018年5月29日 下午3:14:44
* @version: V1.0
* @review: 张彬/2018年5月29日 下午3:14:44
*/
public class Screen {
private static Screen instance = new Screen();
private Screen(){}
public static Screen getInstance(){
return instance;
}
public void up(){
System.out.println("屏幕收起!!!");
}
public void down(){
System.out.println("屏幕放下!!!");
}
}
/**
* Facade(外观角色):家庭影院
* @author: 张彬
* @date: 2018年5月29日 下午3:21:56
* @version: V1.0
* @review: 张彬/2018年5月29日 下午3:21:56
*/
public class HomeTheaterFacade {
private Audio audio;
private DvdPlayer dvdPlayer;
private Light light;
private Projector projector;
private Screen screen;
public HomeTheaterFacade(){
audio = Audio.getInstance();
dvdPlayer = DvdPlayer.getInstance();
light = Light.getInstance();
projector = Projector.getInstance();
screen = Screen.getInstance();
}
public void watchMovie(){
projector.on();
screen.down();
dvdPlayer.on();
audio.on();
light.off();
}
public void endMovie(){
projector.off();
screen.up();
dvdPlayer.off();
audio.off();
light.on();
}
}
/**
* 测试
* 以在家看电影为例,我们要打开DVD,音响,投影仪,放下屏幕,关灯;看完电影后还要一一关闭这些操作
* 通过外观模式引入 家庭影院 的外观角色 解决这个问题
* @author: 张彬
* @date: 2018年5月29日 下午3:34:37
* @version: V1.0
* @review: 张彬/2018年5月29日 下午3:34:37
*/
public class Client {
public static void main(String[] args) {
HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
homeTheaterFacade.watchMovie();
System.out.println("看看看看看看看看看看看看");
homeTheaterFacade.endMovie();
}
}
输出结果:
投影仪打开!!!
屏幕放下!!!
DVD打开!!!
音响打开!!!
灯光关闭!!!
看看看看看看看看看看看看
投影仪关闭!!!
屏幕收起!!!
DVD关闭!!!
音响关闭!!!
灯光打开!!!
总结
外观模式的主要优点如下:(1) 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易。通过引入外观模式,客户端代码将变得很简单,与之关联的对象也很少。
(2) 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。
(3) 一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
外观模式的主要缺点如下:(1) 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活 性。
(2) 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则。
在以下情况下可以考虑使用外观模式:
(1) 当要为访问一系列复杂的子系统提供一个简单入口时可以使用外观模式。
(2) 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性。
(3) 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。