前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、外观模式(Facade Pattern)

1、模式概述

外观模式 是一种 结构型设计模式,它为 复杂的子系统提供一个统一的高层接口,使得子系统更易于使用。 外观(Facade)就像一个“接待员”或“门面”,屏蔽内部细节,只暴露简单、清晰的操作入口。

核心思想“复杂藏在幕后,简单摆在台前。”

外观模式 = 简化入口 + 隐藏复杂

  • 它不是“减少代码”,而是“减少认知负担”。

类比理解:

  • 去医院看病
    • 患者要自己挂号 → 门诊 → 划价 → 缴费 → 取药……流程繁琐;
    • 如果有 导诊护士 统一协调,患者只需说“我头疼”,其余交给她处理——这就是 外观模式
  • 开车:发动机、变速箱、点火系统协同工作,但你只需转动钥匙 + 踩油门,无需懂机械原理。

2、使用场景

外观模式适用于以下情况:

  • 子系统 非常复杂难以理解,需要提供一个简单的入口;
  • 系统中存在 大量相互依赖的类,客户端直接调用容易出错;
  • 你需要 分层架构,将子系统与客户端解耦;
  • 希望 封装遗留系统,对外提供现代化接口。

典型应用

  • 操作系统 API(如 File.open() 封装底层 I/O);
  • 框架中的工具类(如 SpringJdbcTemplate);
  • 微服务网关(Gateway 聚合多个服务调用);
  • 游戏引擎的启动器(一键初始化音频、图形、物理模块)。

3、示例程序:图形绘制外观

1)子系统接口 Shape

/**
 * 子系统公共接口(可选)
 */
public interface Shape {
    void draw();
}

2)子系统实现类

a、圆形

public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Circle::draw()");
    }
}

b、矩形

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Rectangle::draw()");
    }
}

c、正方形

public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Square::draw()");
    }
}

3)外观类 ShapeMaker

/**
 * 外观类(Facade)
 * 封装图形子系统的复杂性,提供简单接口
 */
public class ShapeMaker {
    private final Shape circle;
    private final Shape rectangle;
    private final Shape square;

    public ShapeMaker() {
        // 初始化子系统组件
        this.circle = new Circle();
        this.rectangle = new Rectangle();
        this.square = new Square();
    }

    // 高层接口:隐藏内部调用细节
    public void drawCircle() {
        circle.draw();
    }

    public void drawRectangle() {
        rectangle.draw();
    }

    public void drawSquare() {
        square.draw();
    }

    // 可扩展:组合操作
    public void drawAllShapes() {
        drawCircle();
        drawRectangle();
        drawSquare();
    }
}

4)客户端测试 Main

  • 客户端 无需知道 CircleRectangle 等类的存在
  • 若子系统重构(如改名、拆分),只需修改 ShapeMaker,客户端零改动
/**
 * 客户端(Client)
 * 只与外观类交互,完全 unaware 子系统细节
 */
public class Main {
    public static void main(String[] args) {
        ShapeMaker shapeMaker = new ShapeMaker();

        // 简单调用
        shapeMaker.drawCircle();
        shapeMaker.drawRectangle();
        shapeMaker.drawSquare();

        System.out.println("\n=== 一键绘制所有图形 ===");
        shapeMaker.drawAllShapes();
    }
}

输出:

Circle::draw()
Rectangle::draw()
Square::draw()

=== 一键绘制所有图形 ===
Circle::draw()
Rectangle::draw()
Square::draw()

5)模式角色

  • 客户端 只知道 Facade
  • Facade 知道所有子系统
  • 子系统 彼此可能耦合,但对客户端透明
角色 职责 示例
Facade(外观类) 提供统一的高层接口,封装子系统调用 ShapeMaker
Subsystem Classes(子系统类) 实现具体功能,但对客户端隐藏 Circle, Rectangle, Square
Client(客户端) 通过外观类与子系统交互,不直接访问子系统 Main
                ┌──────────────┐
                │   Client     │
                └───────┬──────┘
                        │ uses
                ┌───────▼──────┐
                │  ShapeMaker  │<──────────┐
                │ (Facade)     │           │
                ├──────────────┤           │
                │ - circle     │           │
                │ - rectangle  │           │
                │ - square     │           │
                └───────┬──────┘           │
                        │                  │
         ┌──────────────┼──────────────────┼──────────────┐
         │              │                  │              │
┌────────▼───────┐ ┌────▼──────────┐ ┌─────▼────────┐ ┌───▼──────────┐
│    Circle      │ │   Rectangle   │ │    Square    │ │   ...其他    │
└────────────────┘ └───────────────┘ └──────────────┘ └──────────────┘
       ▲                  ▲                 ▲
       └──────────────────┴─────────────────┘
                    implements
                   ┌────────────┐
                   │   Shape    │
                   └────────────┘

4、FQA

1)外观模式 vs 建造者模式

  • Builder 是“生产者”:关注 如何一步步造出一个对象
  • Facade 是“接待员”:关注 如何让别人轻松使用一堆现成的对象
模式 目的 是否创建对象 关注点
建造者模式(Builder) 构建复杂对象,支持不同配置生成不同产品 ✅ 创建新对象 对象的构造过程
外观模式(Facade) 简化已有系统的使用,不创建新对象 ❌ 不创建(仅组合已有对象) 系统的使用接口

2)模式优点

  • 降低耦合度:客户端与子系统解耦;
  • 提高易用性:提供简洁、语义清晰的接口;
  • 便于维护:子系统变更不影响客户端;
  • 支持分层开发:前端/后端可通过外观隔离。

3)注意事项

  • 可能违背单一职责原则:外观类承担过多协调逻辑;
  • 过度封装风险:若子系统本身不复杂,使用外观属于冗余;
  • 灵活性降低:客户端无法细粒度控制子系统(如只初始化部分组件)