设计模式-外观模式(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 代理模式

模式 目的 模式架构主要角色 应用场景
外观模式 简化接口,减少系统的复杂性 外观角色和子系统角色 复杂的系统且包含多个子系统,简化系统的接口
代理模式 控制对被代理类的访问 代理角色和目标角色 访问对象前进行一些预处理操作的场景
相关推荐
捕鲸叉4 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
小小小妮子~4 小时前
框架专题:设计模式
设计模式·框架
先睡4 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
Damon_X12 小时前
桥接模式(Bridge Pattern)
设计模式·桥接模式
越甲八千17 小时前
重温设计模式--享元模式
设计模式·享元模式
码农爱java18 小时前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式
越甲八千18 小时前
重温设计模式--中介者模式
windows·设计模式·中介者模式
犬余19 小时前
设计模式之桥接模式:抽象与实现之间的分离艺术
笔记·学习·设计模式·桥接模式
Theodore_102220 小时前
1 软件工程——概述
java·开发语言·算法·设计模式·java-ee·软件工程·个人开发
越甲八千21 小时前
重拾设计模式--组合模式
设计模式·组合模式