基于本文回答
0
评论

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

知识点图片

外观模式(Facade)为复杂的子系统提供一个统一、简化的接口,如同公司的前台。它隐藏了内部实现,降低了客户端与子系统间的耦合,使系统更易于使用。

我们来详细地讲解一下设计模式中的外观模式(Facade Pattern)

核心思想

外观模式的核心思想是:为系统中一组复杂的接口提供一个统一的、高层的、简化的入口。

它隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。这使得子系统更加易于使用。


一句话概括

就像是公司的前台或客服。 你有什么需求(比如找人、投诉、咨询),你不需要知道公司内部复杂的部门结构和人员分工,你只需要告诉前台,前台会帮你处理好一切。这个“前台”就是外观(Facade)。


生活中的例子

  1. 启动电脑:你只需要按一下电源按钮。这个“电源按钮”就是外观。它背后触发了一系列复杂的操作:CPU启动、内存自检、加载操作系统、启动各种服务等等。你完全不需要关心这些底层细节。
  2. 使用智能家居:你对智能音箱说“我回家了”。这个指令就是外观。它会帮你完成一系列动作:打开灯、拉开窗帘、启动空调、播放音乐。你不需要分别去操作每一个设备。
  3. 去医院看病:你只需要去“导诊台”或者挂号处,他们会告诉你接下来该去哪个科室、找哪个医生、去哪里缴费、去哪里拿药。这个“导诊台”就是医院这个复杂系统的外观。

为什么需要外观模式?(解决什么问题)

  1. 降低耦合度:将客户端代码与子系统的具体实现解耦。客户端只需要与外观对象交互,而不需要关心子系统内部的变化。如果子系统升级或替换,只要外观接口不变,客户端代码就无需修改。
  2. 简化客户端使用:一个复杂的系统可能包含大量的类和错综复杂的关系。外观模式提供了一个简单的接口,让客户端更容易地使用这个系统。
  3. 更好的分层:外观模式有助于构建层次化的系统结构。你可以使用外观模式为每个子系统层定义一个入口,从而减少层与层之间的依赖。

外观模式的结构

外观模式包含以下主要角色:

  • Facade(外观)
    • 这是模式的核心。它知道哪些子系统类负责处理请求。
    • 它将客户端的请求代理给适当的子系统对象。
    • 它提供了一个高层接口,简化了对子系统的访问。
  • Subsystem classes(子系统类)
    • 实现子系统的功能。
    • 处理由 Facade 对象委派的任务。
    • 子系统类不知道外观的存在,它们可以被客户端直接调用(但不推荐)。
  • Client(客户端)
    • 通过调用 Facade 的接口来与子系统进行通信。
    • 客户端不需要直接与子系统交互。

结构图如下:

plaintext
+----------+      calls      +----------------------+
|  Client  | ---------------->|       Facade         |
+----------+                  +----------------------+
                                      |
                                      | delegates to
                                      v
          +--------------------------------------------------+
          |                                                  |
+-------------------+   +-------------------+   +-------------------+
|  Subsystem Class A|   |  Subsystem Class B|   |  Subsystem Class C|
+-------------------+   +-------------------+   +-------------------+

代码示例(Java)

我们用上面提到的“智能家居”的例子来实现。

1. 定义子系统类

这些是系统中独立的组件,比如灯、电视、空调。

java
// 子系统1:灯光系统
class Light {
    public void turnOn() {
        System.out.println("灯已打开...");
    }

    public void turnOff() {
        System.out.println("灯已关闭...");
    }
}

// 子系统2:电视系统
class Television {
    public void turnOn() {
        System.out.println("电视已打开...");
    }

    public void turnOff() {
        System.out.println("电视已关闭...");
    }
}

// 子系统3:空调系统
class AirConditioner {
    public void turnOn() {
        System.out.println("空调已打开...");
    }

    public void turnOff() {
        System.out.println("空调已关闭...");
    }
}

2. 创建外观类 (Facade)

这个外观类封装了对所有子系统的操作,并提供了简单的方法如 goHome()leaveHome()

java
// 外观类:智能家居中心
class SmartHomeFacade {
    private Light light;
    private Television television;
    private AirConditioner airConditioner;

    // 在构造函数中初始化所有子系统
    public SmartHomeFacade() {
        this.light = new Light();
        this.television = new Television();
        this.airConditioner = new AirConditioner();
    }

    // 提供一个高层方法,封装了一系列子系统操作
    public void goHome() {
        System.out.println("--- 欢迎回家!正在为您开启设备 ---");
        light.turnOn();
        television.turnOn();
        airConditioner.turnOn();
        System.out.println("--- 设备已全部开启,请享受! ---");
    }

    // 提供另一个高层方法
    public void leaveHome() {
        System.out.println("--- 您要出门了,正在为您关闭设备 ---");
        light.turnOff();
        television.turnOff();
        airConditioner.turnOff();
        System.out.println("--- 设备已全部关闭,一路顺风! ---");
    }
}

3. 客户端调用

客户端现在只需要和 SmartHomeFacade 交互,非常简单。

java
// 客户端
public class Client {
    public static void main(String[] args) {
        // 创建一个智能家居外观实例
        SmartHomeFacade facade = new SmartHomeFacade();

        // 用户回家,只需调用一个方法
        System.out.println("=== 用户回家了 ===");
        facade.goHome();

        System.out.println("\n========================\n");

        // 用户离家,也只需调用一个方法
        System.out.println("=== 用户准备出门 ===");
        facade.leaveHome();
    }
}

输出结果:

plaintext
=== 用户回家了 ===
--- 欢迎回家!正在为您开启设备 ---
灯已打开...
电视已打开...
空调已打开...
--- 设备已全部开启,请享受! ---

========================

=== 用户准备出门 ===
--- 您要出门了,正在为您关闭设备 ---
灯已关闭...
电视已关闭...
空调已关闭...
--- 设备已全部关闭,一路顺风! ---

可以看到,客户端代码非常简洁,完全不知道 Light, Television 这些类的存在。


优缺点

优点

  1. 松散耦合:客户端与子系统之间解耦,使得子系统的组件变化不会影响到客户端。
  2. 简单易用:外观模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统交互。
  3. 提高安全性:可以为子系统设计一个访问的“安全门”,只暴露必要的功能给客户端。
  4. 更好的分层:可以利用外观模式对系统进行分层,层与层之间的交互通过外观进行,使系统结构更加清晰。

缺点

  1. 可能成为上帝类(God Object):如果一个系统被设计得不好,所有的业务逻辑都可能堆砌在外观类中,导致这个类变得异常庞大和复杂。
  2. 不符合开闭原则:当在子系统中增加或删除功能时,可能需要修改外观类的代码,这在一定程度上违背了“开闭原则”(对扩展开放,对修改关闭)。

与其他模式的比较

  • 外观模式 vs. 适配器模式 (Adapter Pattern)

    • 意图不同:外观模式是为了简化接口,而适配器模式是为了转换接口。外观模式是为现有的一组接口提供一个更简单的统一入口;适配器则是将一个类的接口转换成客户端所期望的另一个接口。
    • 解决问题不同:外观模式解决的是“如何让一个复杂的系统变得更容易使用”;适配器模式解决的是“如何让两个不兼容的接口协同工作”。
  • 外观模式 vs. 中介者模式 (Mediator Pattern)

    • 通信方式不同:外观模式是单向的,客户端通过外观调用子系统,但子系统之间通常不通过外观来通信,它们甚至不知道外观的存在。而中介者模式是多向的,它主要用于解耦多个对象之间的复杂网状通信,所有对象都通过中介者来通信。

总结

外观模式是一种非常实用且常见的结构型设计模式。当你遇到一个复杂的系统,或者需要为一个遗留系统提供一个更现代、更简单的接口时,它都是一个绝佳的选择。它的主要目标就是封装复杂性,提供简单性

右滑查看面试常问