4、外观模式
大约 9 分钟
外观模式
外观模式基本介绍
外观模式的定义
外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
- 此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
- 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节
外观模式的结构与实现
外观(Facade)模式的结构比较简单,主要是定义了一个高层接口。它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。

外观(Facade)模式包含以下主要角色。
- 外观(Facade)角色:提供了访问子系统一组接口的统一接口,并将客户端请求委派给相应的子系统对象处理。
- 子系统(Sub System)角色:实现了子系统的具体功能,处理来自外观对象的请求。
- 客户(Client)角色:通过一个外观角色访问各个子系统的功能。
外观模式简单实现
定义抽象接口
/*定义外观角色*/
public interface SystemFacade {
public void dosomething();
}
SystemFacade:定义一组抽象的接口,所有子系统全部实现抽象接口;
具体子系统
//子系统A
public class SubSystemA implements SystemFacade {
@Override
public void dosomething() {
System.out.println("子系统方法A");
}
}
//子系统B
public class SubSystemB implements SystemFacade{
@Override
public void dosomething() {
System.out.println("子系统方法B");
}
}
封装门面
/*定义门面,封装子系统 提供统一的门面访问类*/
public class Facade {
/*被委托的对象*/
SystemFacade facadeA,facadeB;
public Facade() {
facadeA = new SubSystemA();
facadeB = new SubSystemB();
}
//提供给外部访问的方法
public void methodA() {
this.facadeA.dosomething();
}
public void methodB() {
this.facadeB.dosomething();
}
}
Facade是门面类,封装所有子系统的业务动作,对外提供访问接口;
客户端
public class Facade_test_API {
public static void main(String[] args) {
Facade facade = new Facade();
facade.methodA();
facade.methodB();
}
}
外观模式解决影院管理
- 外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口才能达到目的。比如:在 pc 上安装软件的时候经常有一键安装选项(省去选择安装目录、安装的组件等等),还有就是手机的重启功能(把关机和启动合为一个操作)。
- 外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用
场景说明
下面看一个电影院案例,组建一个家庭影院:
- 影院包括,DVD 播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为: 直接用遥控器:统筹各设备开关
- 开爆米花机 放下屏幕 开投影仪 开音响 开DVD,选 dvd
- 去拿爆米花 调暗灯光 播放
- 观影结束后,关闭各种设备
问题分析
- 在 ClientTest 的 main 方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程
- 不利于在 ClientTest 中,去维护对子系统的操作
- 解决思路:定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法ready, play, pause, end ),用来访问子系统中的一群接口
- 也就是说 就是通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节 => 外观模式
定义各个子系统模块
class DVDplayer{
// 一个系统只有一个dvd
private static DVDplayer instance=new DVDplayer();
public static DVDplayer getInstance(){
return instance;
}
// DVD的功能
public void on(){
System.out.println("DVD ON");
}
public void off(){
System.out.println("DVD OF");
}
public void play(){
System.out.println("DVD PLAY");
}
public void pause(){
System.out.println("DVD pause");
}
}
class PopCorn{
// 一个系统只有一个dvd
private static PopCorn instance=new PopCorn();
public static PopCorn getInstance(){
return instance;
}
public void on(){
System.out.println("PopCorn ON");
}
public void off(){
System.out.println("PopCorn OF");
}
public void play(){
System.out.println("PopCorn PLAY");
}
}
class Projector{
// 一个系统只有一个dvd
private static Projector instance=new Projector();
public static Projector getInstance(){
return instance;
}
public void on(){
System.out.println("Projector ON");
}
public void off(){
System.out.println("Projector OF");
}
public void play(){
System.out.println("Projector PLAY");
}
}
class Screen{
// 一个系统只有一个dvd
private static Screen instance=new Screen();
public static Screen getInstance(){
return instance;
}
public void on(){
System.out.println("Screen up");
}
public void off(){
System.out.println("Screen down");
}
public void play(){
System.out.println("Screen PLAY");
}
}
class Stereo{
// 一个系统只有一个dvd
private static Stereo instance=new Stereo();
public static Stereo getInstance(){
return instance;
}
public void on(){
System.out.println("Stereo up");
}
public void off(){
System.out.println("Stereo down");
}
public void play(){
System.out.println("Stereo PLAY");
}
}
class TheaterLight{
// 一个系统只有一个dvd
private static TheaterLight instance=new TheaterLight();
public static TheaterLight getInstance(){
return instance;
}
public void on(){
System.out.println("TheaterLight up");
}
public void off(){
System.out.println("TheaterLight down");
}
public void play(){
System.out.println("TheaterLight PLAY");
}
}
定义影院门面
/*定义门面,里面包含多个子系统引用,并且可以单独操作某一个子系统*/
public class HomeThreaterFacade {
// 定义各个子系统
private DVDplayer dvDplayer;
private PopCorn popCorn;
private Projector projector;
private Screen screen;
private Stereo stereo;
private TheaterLight theaterLight;
public HomeThreaterFacade() {
this.dvDplayer = DVDplayer.getInstance();
this.popCorn = PopCorn.getInstance();
this.projector = Projector.getInstance();
this.screen = Screen.getInstance();
this.stereo = Stereo.getInstance();
this.theaterLight = TheaterLight.getInstance();
}
/*开始启动*/
public void ready(){
popCorn.on();;
projector.on();
screen.on();;
stereo.on();
theaterLight.on();
dvDplayer.on();
}
/*暂停*/
public void pause(){
dvDplayer.pause();
}
/*结束*/
public void end(){
popCorn.off();
projector.off();
screen.off();
stereo.off();
theaterLight.off();
dvDplayer.off();
}
}
影院客户端
public class Facade_dvd_test_API {
public static void main(String[] args) {
HomeThreaterFacade homeThreaterFacade = new HomeThreaterFacade();
homeThreaterFacade.ready();
homeThreaterFacade.end();
}
}
外观模式注意事项
- 外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
- 外观模式对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展
- 通过合理的使用外观模式,可以帮我们更好的划分访问的层次
- 当系统需要进行分层设计时,可以考虑使用 Facade 模式
- 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade 类交互,提高复用性
- 不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。
外观模式的优缺点
外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点。
- 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
- 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
- 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
外观(Facade)模式的主要缺点如下。
- 不能很好地限制客户使用子系统类,很容易带来未知风险。
- 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”
应用场景
通常在以下情况下可以考虑使用外观模式。
- 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
- 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
- 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。
外观模式扩展
在外观模式中,当增加或移除子系统时需要修改外观类,这违背了“开闭原则”。如果引入抽象外观类,则在一定程度上解决了该问题,其结构图如图所示:

代码实现
public class FacadePattern
{
public static void main(String[] args)
{
Facade f=new FacadeImpl1();
f.method();
f=new FacadeImpl2();
f.method();
}
}
interface Facade {
public void method();
}
//外观角色
class FacadeImpl1 implements Facede
{
private SubSystem01 obj1=new SubSystem01();
private SubSystem02 obj2=new SubSystem02();
private SubSystem03 obj3=new SubSystem03();
public void method()
{
obj1.method1();
obj2.method2();
obj3.method3();
}
}
//外观角色
class FacadeImpl1 implements Facede
{
private SubSystem02 obj2=new SubSystem02();
private SubSystem03 obj3=new SubSystem03();
private SubSystem04 obj4=new SubSystem04();
public void method()
{
obj2.method2();
obj3.method3();
obj4.method4();
}
}
//子系统角色
class SubSystem01
{
public void method1()
{
System.out.println("子系统01的method1()被调用!");
}
}
//子系统角色
class SubSystem02
{
public void method2()
{
System.out.println("子系统02的method2()被调用!");
}
}
//子系统角色
class SubSystem03
{
public void method3()
{
System.out.println("子系统03的method3()被调用!");
}
}
//子系统角色
class SubSystem04
{
public void method4()
{
System.out.println("子系统04的method4()被调用!");
}
}