设计模式-外观模式(Facade)

1. 概念

  • 外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用于访问子系统中的一群接口。外观模式的主要目的是隐藏系统的复杂性,通过定义一个高层级的接口,使得子系统更容易被使用。在外观模式中,客户端不与每个子系统进行复杂的交互,而是只与提供接口的外观类进行交互

2. 原理结构图

  • 外观角色(Facade):这是外观模式的核心角色,它提供了一个简化的接口,用于访问子系统中的功能。外观类的作用是封装复杂的子系统操作,让外部客户端无需了解内部细节就能进行交互。
  • 子系统角色(Subsystem):这些是实际执行具体任务的类或模块。它们可能包含多个类和更复杂的逻辑,对于客户端来说,直接与这些子系统交互可能会非常复杂。
  • 客户角色(Client):客户端使用外观类提供的接口与子系统进行交互。通过这种方式,客户端可以简化其代码,因为它只需要与外观类打交道,而不是直接与复杂的子系统打交道。

3. 代码示例

3.1 示例1
  • 有一个复杂的音频播放系统,其中包含多个组件,如CD播放器、MP3播放器、音量控制器等。每个组件都有自己的接口和实现。我们希望提供一个简化的接口,让用户能够轻松地播放音乐,而无需关心底层组件的细节。
java 复制代码
// CD播放器接口  
interface CDPlayer {  
    void play();  
    void stop();  
}  
  
// CD播放器实现  
class RealCDPlayer implements CDPlayer {  
    @Override  
    public void play() {  
        System.out.println("Playing CD...");  
    }  
  
    @Override  
    public void stop() {  
        System.out.println("Stopping CD...");  
    }  
}  
  
// MP3播放器接口  
interface MP3Player {  
    void play();  
    void stop();  
}  
  
// MP3播放器实现  
class RealMP3Player implements MP3Player {  
    @Override  
    public void play() {  
        System.out.println("Playing MP3...");  
    }  
  
    @Override  
    public void stop() {  
        System.out.println("Stopping MP3...");  
    }  
}  
  
// 音量控制器接口  
interface VolumeControl {  
    void increaseVolume();  
    void decreaseVolume();  
}  
  
// 音量控制器实现  
class RealVolumeControl implements VolumeControl {  
    @Override  
    public void increaseVolume() {  
        System.out.println("Increasing volume...");  
    }  
  
    @Override  
    public void decreaseVolume() {  
        System.out.println("Decreasing volume...");  
    }  
}

class AudioPlayerFacade {  
    private CDPlayer cdPlayer;  
    private MP3Player mp3Player;  
    private VolumeControl volumeControl;  
  
    public AudioPlayerFacade() {  
        cdPlayer = new RealCDPlayer();  
        mp3Player = new RealMP3Player();  
        volumeControl = new RealVolumeControl();  
    }  
  
    public void playCD() {  
        cdPlayer.play();  
    }  
  
    public void playMP3() {  
        mp3Player.play();  
    }  
  
    public void stop() {  
        cdPlayer.stop();  
        mp3Player.stop();  
    }  
  
    public void increaseVolume() {  
        volumeControl.increaseVolume();  
    }  
  
    public void decreaseVolume() {  
        volumeControl.decreaseVolume();  
    }  
}
public class AudioPlayerClient {  
    public static void main(String[] args) {  
        AudioPlayerFacade audioPlayerFacade = new AudioPlayerFacade();  
  
        audioPlayerFacade.playCD();  
        audioPlayerFacade.increaseVolume();  
        audioPlayerFacade.playMP3();  
        audioPlayerFacade.decreaseVolume();  
        audioPlayerFacade.stop();  
    }  
}
  • 输出
java 复制代码
Playing CD...
Increasing volume...
Playing MP3...
Decreasing volume...
Stopping CD...
Stopping MP3...
  • 在这个示例中,AudioPlayerFacade 类充当外观类,它封装了 CDPlayer、MP3Player 和 VolumeControl 组件的实例,并提供了简化的接口供客户端使用。客户端代码只需与外观类进行交互,而无需关心底层组件的实现细节。这使得代码更加简洁和易于维护。

3.2 示例2
  • 智能家居系统是一个很好的例子,用于说明外观模式如何简化复杂系统的使用。在这个场景中,用户可能想要控制多个设备,如灯、电视、空调等,每个设备都有自己的控制接口和实现。通过使用外观模式,我们可以提供一个统一的接口,使得用户能够更容易地控制这些设备。
java 复制代码
// 灯的接口  
interface Light {  
    void turnOn();  
    void turnOff();  
}  
  
// 真实的灯实现  
class RealLight implements Light {  
    @Override  
    public void turnOn() {  
        System.out.println("Light is turned on.");  
    }  
  
    @Override  
    public void turnOff() {  
        System.out.println("Light is turned off.");  
    }  
}  
  
// 电视的接口  
interface TV {  
    void turnOn();  
    void turnOff();  
    void changeChannel(int channel);  
}  
  
// 真实的电视实现  
class RealTV implements TV {  
    @Override  
    public void turnOn() {  
        System.out.println("TV is turned on.");  
    }  
  
    @Override  
    public void turnOff() {  
        System.out.println("TV is turned off.");  
    }  
  
    @Override  
    public void changeChannel(int channel) {  
        System.out.println("TV channel changed to: " + channel);  
    }  
}  
  
// 空调的接口  
interface AirConditioner {  
    void turnOn();  
    void turnOff();  
    void setTemperature(int temperature);  
}  
  
// 真实的空调实现  
class RealAirConditioner implements AirConditioner {  
    @Override  
    public void turnOn() {  
        System.out.println("Air conditioner is turned on.");  
    }  
  
    @Override  
    public void turnOff() {  
        System.out.println("Air conditioner is turned off.");  
    }  
  
    @Override  
    public void setTemperature(int temperature) {  
        System.out.println("Air conditioner set to: " + temperature + " degrees.");  
    }  
}

class SmartHomeFacade {  
    private Light light;  
    private TV tv;  
    private AirConditioner airConditioner;  
  
    public SmartHomeFacade() {  
        light = new RealLight();  
        tv = new RealTV();  
        airConditioner = new RealAirConditioner();  
    }  
  
    public void turnOnLight() {  
        light.turnOn();  
    }  
  
    public void turnOffLight() {  
        light.turnOff();  
    }  
  
    public void turnOnTV() {  
        tv.turnOn();  
    }  
  
    public void turnOffTV() {  
        tv.turnOff();  
    }  
  
    public void changeTVChannel(int channel) {  
        tv.changeChannel(channel);  
    }  
  
    public void turnOnAirConditioner() {  
        airConditioner.turnOn();  
    }  
  
    public void turnOffAirConditioner() {  
        airConditioner.turnOff();  
    }  
  
    public void setAirConditionerTemperature(int temperature) {  
        airConditioner.setTemperature(temperature);  
    }  
}

public class SmartHomeClient {  
    public static void main(String[] args) {  
        SmartHomeFacade smartHomeFacade = new SmartHomeFacade();  
  
        // 控制灯  
        smartHomeFacade.turnOnLight();  
        // 做一些其他事情...  
        smartHomeFacade.turnOffLight();  
  
        // 控制电视  
        smartHomeFacade.turnOnTV();  
        smartHomeFacade.changeTVChannel(5);  
        // 做一些其他事情...  
        smartHomeFacade.turnOffTV();  
  
        // 控制空调  
        smartHomeFacade.turnOnAirConditioner();  
        smartHomeFacade.setAirConditionerTemperature(24);  
        // 做一些其他事情...  
        smartHomeFacade.turnOffAirConditioner();  
    }  
}
  • 将看到如下输出:
java 复制代码
Light is turned on.
Light is turned off.
TV is turned on.
TV channel changed to: 5
TV is turned off.
Air conditioner is turned on.
Air conditioner set to: 24 degrees.
Air conditioner is turned off.
  • 在这个例子中,SmartHomeFacade 类充当外观类,它封装了 Light、TV 和 AirConditioner 设备的实例,并提供了统一的接口供客户端使用。客户端代码只需与外观类进行交互,而无需关心底层设备的实现细节。这简化了智能家居系统的使用,并使得

4. 优缺点

  • 主要作用
    • 为复杂的子系统提供一个统一、简化的接口,以便更容易地访问子系统中的功能。
  • 优点
    • 简化接口:外观模式为子系统提供了一个简单、统一的接口,使得客户端能够更方便地访问和操作子系统的功能。这有助于减少客户端需要与之交互的接口数量,降低了系统的复杂性。
    • 解耦子系统与客户端:外观模式隐藏了子系统的内部结构和复杂性,客户端只需与外观类进行交互,无需了解子系统的具体实现。这种解耦使得子系统的变化对客户端的影响最小化,提高了系统的可维护性和可扩展性。
    • 提高系统的安全性:通过限制客户端对子系统的直接访问,外观模式有助于防止客户端对子系统进行不当操作或破坏。外观类可以对外部请求进行验证和过滤,确保只有合法的请求能够到达子系统。
    • 灵活性增强:当需要添加新的子系统功能或修改现有功能时,由于客户端只与外观类交互,因此只需修改外观类而无需修改所有客户端代码。这增强了系统的灵活性和可扩展性。
    • 易于管理和控制:外观类可以统一管理和控制对子系统的访问,这有助于维护系统的一致性和完整性。同时,外观类还可以对子系统的操作进行日志记录、性能监控等,方便后续的管理和维护工作。
  • 缺点
    • 违背开闭原则:当需要添加新的子系统或者修改现有子系统时,可能需要更改外观类的实现,这违反了面向对象设计中的开闭原则。
    • 隐藏细节:外观模式隐藏了子系统细节,可能使客户端难以深入了解或定制子系统行为。
    • 增加系统的复杂性:虽然外观模式简化了客户端与系统的交互,但在系统中引入了一个额外的层次,这可能会增加系统的复杂性。
    • 潜在的性能问题:由于所有请求都通过外观类进行转发,这可能会成为性能瓶颈,尤其是在高并发的场景下。

5. 应用场景

5.1 主要包括以下几个方面
  1. 系统复杂度较高:当系统的某一子系统变得过于复杂,不容易使用时,可以使用外观模式来进行简化。外观模式可以将系统的复杂性内部化,对外提供一个简单的接口,使得使用者更加容易使用。
  2. 存在多个复杂接口:当系统中存在多个接口之间的依赖关系比较复杂时,外观模式可以将这些接口进行封装,将复杂性进行内部化,从而简化其使用和维护。
  3. 需要对外封闭:当系统需要对外封闭,外界只能通过一个统一的接口来访问系统时,外观模式非常适用。它可以提供一个统一的接口,用于访问子系统中的一群接口,从而有效提高系统的安全性。
  4. 系统重构:当系统需要进行重构,需要对原有的代码进行优化和改进时,外观模式也能发挥重要作用。它可以将系统功能进行重组,减少耦合,使得代码更加易于理解和维护,从而提高系统的灵活性和可扩展性。
  5. 多层系统构建:在构建多层系统时,外观模式可以作为每层的入口,简化层间调用。例如,在经典的三层架构中,可以在数据访问层和业务逻辑层、业务逻辑层和表示层之间建立外观,降低耦合度。
  6. 维护遗留系统:对于难以维护和扩展的遗留大型系统,如果它包含重要的功能且新的需求开发必须依赖它,使用外观模式可以提供一个简洁的接口,方便新需求的开发。

5.2 实际应用
  1. 操作系统界面:操作系统提供一个统一的界面(外观类),用户通过这个界面可以方便地使用各种功能,而不需要关心底层子系统的具体实现。这个界面简化了用户的操作,隐藏了子系统的复杂性。
  2. 智能家居系统:智能家居系统可以设计一个统一的控制界面(外观类),用户通过这个界面可以控制所有智能设备,而不需要单独操作每个设备的控制接口。这个界面简化了用户的操作,提高了使用的便捷性。
  3. 游戏开发:游戏引擎可以提供一个简单的接口(外观类),游戏开发者通过这个接口可以方便地使用游戏引擎的各种功能,而不需要直接与底层的子系统打交道。这样,游戏开发者可以更加专注于游戏的逻辑和玩法设计,而不用关心底层的实现细节。
  4. 企业级应用:企业级应用可以设计一个统一的业务操作界面(外观类),用户通过这个界面可以完成跨模块的业务操作,而不需要了解每个模块的具体实现和子系统之间的复杂关系。这样,用户的操作变得更加简单和高效。

6. JDK中的使用

  • Java的集合框架:Java集合框架提供了丰富的集合类,如ArrayList、HashSet等,以及各种迭代器接口。为了简化对这些集合类的操作,Java提供了诸如Collections这样的工具类,它作为集合框架的Facade,提供了静态方法对集合进行排序、搜索、线程安全化等操作。这样,客户端代码就不需要直接与各种集合类交互,而是通过Collections这个统一的接口进行操作,降低了系统的复杂性。
  • Java I/O流:Java的I/O流体系非常复杂,涉及字节流、字符流、输入流、输出流等多种类型。为了方便用户使用,Java提供了诸如Files这样的工具类作为Facade,它提供了静态方法用于文件的读写、复制、移动等操作。这样,用户就不需要深入了解I/O流的各种细节,只需要调用Files类提供的方法即可。
  • Java的JDBC API:JDBC API用于Java程序与数据库进行交互。为了简化数据库操作,Java提供了DataSource作为Facade,它封装了数据库连接池的管理和数据库连接的创建过程。用户只需要通过DataSource获取数据库连接,然后执行SQL语句即可,无需关心连接池的具体实现和细节。

7. 注意事项

  • 明确接口简化目标:外观模式的主要目的是简化复杂系统的接口,因此在设计Facade类时,需要明确哪些接口需要被简化,确保Facade类提供的接口既简洁又能够满足客户端的需求。
  • 避免过度封装:虽然外观模式通过封装子系统的复杂性来简化接口,但过度封装可能导致Facade类过于庞大和复杂,反而增加了系统的维护难度。因此,在封装时要适度,避免将过多子系统的细节暴露给Facade类。
  • 保持子系统的独立性:使用外观模式时,应确保子系统之间的独立性。Facade类不应该直接依赖于子系统的具体实现,而应该通过接口或抽象类与子系统交互。这样可以降低Facade类与子系统之间的耦合度,使得子系统更易于维护和扩展。
  • 考虑系统的可扩展性:在设计Facade类时,应预留一定的扩展空间。随着系统的发展,可能需要添加新的功能或修改现有功能。如果Facade类的设计过于僵化,将难以适应这些变化。因此,在设计时应考虑到未来可能的扩展需求。
  • 注意性能问题:虽然外观模式可以简化接口和降低耦合度,但也可能引入性能问题。如果Facade类中的方法涉及大量计算或资源消耗,那么频繁调用这些方法可能导致性能下降。因此,在使用外观模式时,需要关注性能问题,确保Facade类的实现是高效的。

8. 外观模式 VS 代理模式

模式 目的 模式架构主要角色 应用场景
外观模式 简化接口,减少系统的复杂性 外观角色和子系统角色 复杂的系统且包含多个子系统,简化系统的接口
代理模式 控制对被代理类的访问 代理角色和目标角色 访问对象前进行一些预处理操作的场景
相关推荐
matrixlzp3 小时前
Java 责任链模式 减少 if else 实战案例
java·设计模式
编程、小哥哥6 小时前
设计模式之组合模式(营销差异化人群发券,决策树引擎搭建场景)
决策树·设计模式·组合模式
hxj..7 小时前
【设计模式】外观模式
java·设计模式·外观模式
吾与谁归in7 小时前
【C#设计模式(10)——装饰器模式(Decorator Pattern)】
设计模式·c#·装饰器模式
无敌岩雀8 小时前
C++设计模式行为模式———命令模式
c++·设计模式·命令模式
In_life 在生活18 小时前
设计模式(四)装饰器模式与命令模式
设计模式
瞎姬霸爱.18 小时前
设计模式-七个基本原则之一-接口隔离原则 + SpringBoot案例
设计模式·接口隔离原则
鬣主任19 小时前
Spring设计模式
java·spring boot·设计模式
程序员小海绵【vincewm】21 小时前
【设计模式】结合Tomcat源码,分析外观模式/门面模式的特性和应用场景
设计模式·tomcat·源码·外观模式·1024程序员节·门面模式
丶白泽21 小时前
重修设计模式-行为型-命令模式
设计模式·命令模式