外观模式(门面模式)
外观模式介绍
定义
通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。 该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
好比我们去封装工具类。
优点
- 简化客户端的调用
- 代码复用,整合
应用场景
- 需要使用复杂的子系统,但想避免重复且复杂的调用过程
- 子系统需要组织成层结构时
经典案例
org.apache.catalina.connector.RequestFacade
外观模式结构与实现
其实外观模式就是在底层上封装一层接口,避免上层用户直接调用底层接口,提供一些简单的接口给用户使用!
结构
可以通过添加一个抽象外观角色来遵循开闭原则
外观(Facade)角色:为多个子系统对外提供一个共同的接口。
子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
客户(Client)角色:通过一个外观角色访问各个子系统的功能。
模板实现
本来我们需要实现一个功能需要依次执行三个系统类的三个方法,用户使用非常麻烦,因此可以封装一层 外观 类,它来负责与系统交互,用户只需要调用它提供的简单接口即可实现复杂的系统功能!!!
/**
* 外观角色
*/
class Facade {
private SubSystem01 subSystem01 = new SubSystem01();
private SubSystem02 subSystem02 = new SubSystem02();
private SubSystem03 subSystem03 = new SubSystem03();
public void method() {
subSystem01.method1();
subSystem02.method2();
subSystem03.method3();
}
}
/**
* 子系统角色1
*/
class SubSystem01 {
public void method1() {
System.out.println("子系统01的method1()被调用!");
}
}
/**
* 子系统角色2
*/
class SubSystem02 {
public void method2() {
System.out.println("子系统02的method2()被调用!");
}
}
/**
* 子系统角色3
*/
class SubSystem03 {
public void method3() {
System.out.println("子系统03的method3()被调用!");
}
}
/**
* 客户端角色
*/
public class FacadeTemplateTest {
public static void main(String[] args) {
Facade f = new Facade();
f.method();
}
}
/*
子系统01的method1()被调用!
子系统02的method2()被调用!
子系统03的method3()被调用!
*/
示例 DVD播放电影
这个示例中我添加了 抽象外观角色,是为了遵循开闭原则,便于以后在不改变原有类的前提下扩展外观角色!
播放电影需要涉及到 DVD机、显示屏、音箱三个子系统,避免用户接触底层系统,创建外观角色提供简单的接口操作电影的开启、暂停与关闭
/**
* 子系统3
* 用于声音播放
*/
class SoundBox {
private boolean isRun;
public void on() {
if (!isRun) {
System.out.println("开启音箱");
}
}
public void off() {
if (isRun) {
System.out.println("关闭音箱");
}
}
}
/**
* 子系统2 屏幕
* 用于画面显示
*/
class Screen {
private boolean isRun;
public void on() {
if (!isRun) {
isRun = true;
System.out.println("开启显示屏");
}
}
public void off() {
if (isRun) {
isRun = false;
System.out.println("关闭显示屏");
}
}
}
/**
* 子系统1 DVD
* 用于读取光盘
*/
class DVD {
private boolean isOpen;
private boolean hasCD;
private boolean isScanning;
public boolean isScanning() {
return isScanning;
}
public void on() {
if (!isOpen) {
isOpen = true;
System.out.println("打开DVD");
}
}
public void put() {
if (!hasCD) {
hasCD = true;
System.out.println("放入光碟");
} else {
System.out.println("已经有光盘了");
}
}
public void take() {
if (hasCD) {
hasCD = false;
System.out.println("取出光碟");
} else {
System.out.println("DVD中不存在光盘");
}
}
public void scan() {
if (!isScanning) {
isScanning = true;
System.out.println("DVD读取光盘");
}
}
public void pause() {
if (isScanning) {
isScanning = false;
System.out.println("DVD暂停读取光盘");
}
}
public void off() {
if (isOpen) {
isOpen = false;
System.out.println("关闭DVD");
}
}
}
/**
* 抽象门面:电影播放门面接口
*/
interface MovieFacade {
/**
* 播放电影
*/
void playMovie();
/**
* 暂停电影播放
*/
void pauseMovie();
/**
* 关闭电影播放
*/
void closeMovie();
}
/**
* 具体门面:电影播放门面实现类
*/
class MovieFacadeConcrete implements MovieFacade {
private DVD dvd = new DVD();
private SoundBox soundBox = new SoundBox();
private Screen screen = new Screen();
@Override
public void playMovie() {
// 开启各个设备
dvd.on();
soundBox.on();
screen.on();
// 放入光碟
dvd.put();
// 读取光碟
dvd.scan();
}
@Override
public void pauseMovie() {
if (dvd.isScanning()) {
dvd.pause();
} else {
dvd.scan();
}
}
@Override
public void closeMovie() {
// 暂停读取后再取出光盘
if (dvd.isScanning()) {
dvd.pause();
}
dvd.take();
// 关闭各个设备
screen.off();
soundBox.off();
dvd.off();
}
}
/**
* 客户端
*/
public class MovieClient {
public static void main(String[] args) {
MovieFacade movie = new MovieFacadeConcrete();
movie.playMovie();// 开启设备并播放电影
System.out.println("----------------");
movie.pauseMovie();// 暂停播放
System.out.println("----------------");
movie.pauseMovie();// 继续播放
System.out.println("----------------");
movie.closeMovie();// 停止播放电影并关闭设备
/*
* 打开DVD
* 开启音箱
* 开启显示屏
* 放入光碟
* DVD读取光盘
* ----------------
* DVD暂停读取光盘
* ----------------
* DVD读取光盘
* ----------------
* DVD暂停读取光盘
* 取出光碟
* 关闭显示屏
* 关闭DVD
*/
}
}