系列定位:软考软件设计师 / 系统架构设计师 --- 结构型模式专题第 4 讲
考察分值:上午题 1-2 分,下午题常作为代码填空或类图识别出现
难度等级:⭐⭐☆☆☆(概念最直观,但与其他模式的辨析是失分点)
一、考纲定位与模式定义
1.1 考纲要求
外观模式在软考中属于 结构型模式 的入门内容。考察形式包括:
-
上午选择题:判断场景描述所属模式;识别外观模式与适配器模式、代理模式的区别;判断外观类是否屏蔽了子系统的内部细节
-
下午设计题:补全 Facade 类中对多个子系统方法的调用顺序;补全客户端通过 Facade 调用而非直接调用子系统的代码;识别类图中 Facade 与子系统之间的依赖/关联关系
1.2 模式定义
外观模式:为子系统中的一组接口提供一个统一的高层接口,这个接口使得子系统更加容易使用。
核心意图 :当一个子系统由多个类组成,且这些类的调用顺序复杂、相互依赖时,客户端直接与子系统交互的成本很高(需要了解每个子系统类的接口、调用顺序、依赖关系)。外观模式引入一个 "门面"(Facade) 类,将子系统的复杂调用逻辑封装在 Facade 内部,客户端只需与 Facade 打交道,无需关心子系统的内部细节。
通俗理解:
你去餐厅吃饭,后厨有切配、炒菜、装盘、传菜等多个岗位(子系统)。作为顾客(客户端),你不需要跑到后厨指挥每个岗位 "先切配、再炒菜、再装盘",你只需要对服务员(Facade)说 "来一份宫保鸡丁",服务员 internally 协调后厨完成所有步骤,最终把菜端给你。服务员就是后厨子系统的 "门面"。
关键原则 :外观模式不阻止客户端直接访问子系统,只是提供了一个 更简单的入口。如果客户端需要更细粒度的控制,仍然可以直接调用子系统类。
二、UML 类图与角色划分
┌─────────────────────────────────────────────────────────┐
│ Client │
│ + main() │
│ ├── Facade facade = new Facade(); │
│ └── facade.doSomething(); // 一键调用,无需关心内部 │
└───────────────────────┬─────────────────────────────────┘
│ uses
▼
┌─────────────────────────────────────────────────────────┐
│ Facade │
│ ── 门面类:封装子系统的复杂调用逻辑 ── │
│ - subA: SubsystemA │
│ - subB: SubsystemB │
│ - subC: SubsystemC │
│ + doSomething() { │
│ subA.operationA(); │
│ subB.operationB(); │
│ subC.operationC(); │
│ } │
└───────────────────────┬─────────────────────────────────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ SubsystemA │ │ SubsystemB │ │ SubsystemC │
│ + operationA() │ │ + operationB() │ │ + operationC() │
│ (子系统A) │ │ (子系统B) │ │ (子系统C) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
| 角色 | 职责 | 软考填空关键词 |
|---|---|---|
| Facade(外观) | 封装子系统的复杂调用逻辑,提供一个简洁的高层接口 | class + 组合多个子系统引用 + 统一方法 |
| Subsystem(子系统) | 子系统内部的各个类,完成具体的子功能 | 多个独立的类,各自有特定方法 |
| Client(客户端) | 通过 Facade 访问子系统,不直接与子系统类交互 | new Facade() + facade.method() |
类图识别要点 :软考类图中,外观模式的核心特征是存在一个 Facade 类,它同时与多个子系统类有关联/依赖关系(箭头从 Facade 指向多个 Subsystem),而 Client 只与 Facade 交互。如果类图里只有一个类连接多个子系统,且 Client 不直接连子系统 → 外观。如果 Client 直接连子系统,且有一个类做接口转换 → 适配器。
三、场景一:家庭影院一键观影(最经典的外观模式)
业务背景:家庭影院子系统包含多个设备:投影仪(Projector)、音响(SoundSystem)、播放器(DVDPlayer)、灯光(Light)。要看一场电影,需要按顺序操作:关灯 → 放下幕布 → 打开投影仪 → 打开音响 → 放入光盘 → 播放。如果客户端直接操作每个设备,调用顺序复杂且容易出错。使用外观模式封装 "一键观影" 流程。
说明:这是外观模式最经典的例子,也是软考下午题最爱考的 "将多个子系统调用封装为一个方法" 场景。
3.1 代码实现
java
// Subsystem:投影仪
public class Projector {
public void on() {
System.out.println("投影仪:开启");
}
public void setInput(String input) {
System.out.println("投影仪:切换到 " + input + " 输入源");
}
public void off() {
System.out.println("投影仪:关闭");
}
}
// Subsystem:音响系统
public class SoundSystem {
public void on() {
System.out.println("音响:开启");
}
public void setVolume(int level) {
System.out.println("音响:音量调至 " + level);
}
public void off() {
System.out.println("音响:关闭");
}
}
// Subsystem:DVD 播放器
public class DVDPlayer {
public void on() {
System.out.println("DVD 播放器:开启");
}
public void play(String movie) {
System.out.println("DVD 播放器:开始播放《" + movie + "》");
}
public void off() {
System.out.println("DVD 播放器:关闭");
}
}
// Subsystem:灯光控制
public class Light {
public void dim() {
System.out.println("灯光:调暗");
}
public void bright() {
System.out.println("灯光:调亮");
}
}
// Subsystem:幕布
public class Screen {
public void down() {
System.out.println("幕布:放下");
}
public void up() {
System.out.println("幕布:收起");
}
}
// Facade:家庭影院外观
public class HomeTheaterFacade {
private Projector projector;
private SoundSystem sound;
private DVDPlayer dvd;
private Light light;
private Screen screen;
public HomeTheaterFacade(Projector projector, SoundSystem sound,
DVDPlayer dvd, Light light, Screen screen) {
this.projector = projector;
this.sound = sound;
this.dvd = dvd;
this.light = light;
this.screen = screen;
}
public void watchMovie(String movie) {
System.out.println("===== 准备观影 =====");
light.dim();
screen.down();
projector.on();
projector.setInput("DVD");
sound.on();
sound.setVolume(20);
dvd.on();
dvd.play(movie);
System.out.println("===== 开始观影 =====\n");
}
public void endMovie() {
System.out.println("===== 结束观影 =====");
dvd.off();
sound.off();
projector.off();
screen.up();
light.bright();
System.out.println("===== 已恢复 =====\n");
}
}
// Client:客户端
public class Client {
public static void main(String[] args) {
HomeTheaterFacade theater = new HomeTheaterFacade(
new Projector(),
new SoundSystem(),
new DVDPlayer(),
new Light(),
new Screen()
);
theater.watchMovie("星际穿越");
theater.endMovie();
}
}
关键体会 :watchMovie() 方法内部封装了 6 个子系统调用,按固定顺序执行。客户端只需调用 watchMovie("星际穿越"),完全不需要知道投影仪、音响、DVD 等子系统的存在。这就是 "门面" 的价值 ------ 简化接口,降低耦合。
四、场景二:电商订单处理系统(业务子系统封装)
业务背景:电商系统下单流程涉及多个子系统:库存检查(Inventory)、支付处理(Payment)、物流下单(Shipping)、邮件通知(Notification)。一个完整的下单流程需要:检查库存 → 扣减库存 → 处理支付 → 创建物流单 → 发送确认邮件。使用外观模式封装 "一键下单" 流程。
说明:这是软考下午题中常见的 "业务流程封装" 场景,Facade 的方法对应一个完整的业务流程。
4.1 代码实现
java
java
// Subsystem:库存系统
public class InventoryService {
public boolean checkStock(String productId, int quantity) {
System.out.println("[库存] 检查商品 " + productId + " 库存,数量: " + quantity);
return true;
}
public void deductStock(String productId, int quantity) {
System.out.println("[库存] 扣减商品 " + productId + " 库存 " + quantity + " 件");
}
public void restoreStock(String productId, int quantity) {
System.out.println("[库存] 恢复商品 " + productId + " 库存 " + quantity + " 件");
}
}
// Subsystem:支付系统
public class PaymentService {
public boolean processPayment(String orderId, double amount) {
System.out.println("[支付] 处理订单 " + orderId + " 支付,金额: ¥" + amount);
return true;
}
public void refund(String orderId) {
System.out.println("[支付] 订单 " + orderId + " 退款完成");
}
}
// Subsystem:物流系统
public class ShippingService {
public String createShippingOrder(String orderId, String address) {
String trackingNo = "SF" + System.currentTimeMillis();
System.out.println("[物流] 创建运单,订单: " + orderId + ", 单号: " + trackingNo);
return trackingNo;
}
}
// Subsystem:通知系统
public class NotificationService {
public void sendOrderConfirmation(String email, String orderId) {
System.out.println("[通知] 发送订单确认邮件到 " + email + ",订单号: " + orderId);
}
}
// Facade:订单处理外观
public class OrderFacade {
private InventoryService inventory;
private PaymentService payment;
private ShippingService shipping;
private NotificationService notification;
public OrderFacade() {
this.inventory = new InventoryService();
this.payment = new PaymentService();
this.shipping = new ShippingService();
this.notification = new NotificationService();
}
public boolean placeOrder(String productId, int quantity,
double amount, String email, String address) {
String orderId = "ORD" + System.currentTimeMillis();
try {
if (!inventory.checkStock(productId, quantity)) {
System.out.println("下单失败:库存不足");
return false;
}
inventory.deductStock(productId, quantity);
if (!payment.processPayment(orderId, amount)) {
inventory.restoreStock(productId, quantity);
System.out.println("下单失败:支付未成功");
return false;
}
String trackingNo = shipping.createShippingOrder(orderId, address);
notification.sendOrderConfirmation(email, orderId);
System.out.println("下单成功!订单号: " + orderId + ",运单号: " + trackingNo);
return true;
} catch (Exception e) {
inventory.restoreStock(productId, quantity);
System.out.println("下单失败:系统异常");
return false;
}
}
}
// Client
public class Client {
public static void main(String[] args) {
OrderFacade orderFacade = new OrderFacade();
boolean success = orderFacade.placeOrder(
"SKU1001",
2,
599.0,
"user@example.com",
"北京市海淀区"
);
}
}
关键体会 :Facade 不仅封装了调用顺序,还处理了 异常回滚(支付失败时恢复库存)。这是实际开发中外观模式的常见用法 ------ Facade 作为一个 "事务协调者",保证子系统调用的一致性。
五、场景三:数据库操作外观(简化复杂 API)
业务背景 :JDBC 操作数据库需要多个步骤:加载驱动 → 获取连接 → 创建语句 → 执行 SQL → 处理结果集 → 关闭资源。使用外观模式封装常用的 CRUD 操作,让客户端只需调用 query() 或 update() 即可。
说明:这是实际开发中外观模式的简化应用,也是软考上午题中 "简化 API" 场景的典型代表。
5.1 代码实现
java
import java.sql.*;
// Facade:数据库操作外观
public class DatabaseFacade {
private String url;
private String user;
private String password;
public DatabaseFacade(String url, String user, String password) {
this.url = url;
this.user = user;
this.password = password;
}
public ResultSet query(String sql) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
return rs;
} catch (SQLException e) {
throw new RuntimeException("查询失败", e);
}
}
public int update(String sql) {
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement()) {
return stmt.executeUpdate(sql);
} catch (SQLException e) {
throw new RuntimeException("更新失败", e);
}
}
public boolean executeTransaction(String... sqls) {
try (Connection conn = DriverManager.getConnection(url, user, password)) {
conn.setAutoCommit(false);
try (Statement stmt = conn.createStatement()) {
for (String sql : sqls) {
stmt.executeUpdate(sql);
}
conn.commit();
return true;
} catch (SQLException e) {
conn.rollback();
return false;
}
} catch (SQLException e) {
throw new RuntimeException("事务执行失败", e);
}
}
}
// Client
public class Client {
public static void main(String[] args) {
DatabaseFacade db = new DatabaseFacade(
"jdbc:mysql://localhost:3306/test", "root", "123456"
);
int rows = db.update("UPDATE users SET status = 1 WHERE id = 1001");
System.out.println("影响行数: " + rows);
boolean ok = db.executeTransaction(
"UPDATE account SET balance = balance - 100 WHERE id = 1",
"UPDATE account SET balance = balance + 100 WHERE id = 2"
);
System.out.println("转账结果: " + ok);
}
}
六、三种场景对比与演进思路
| 维度 | 场景一:家庭影院 | 场景二:电商下单 | 场景三:数据库操作 |
|---|---|---|---|
| 子系统数量 | 5 个(投影仪、音响、DVD、灯光、幕布) | 4 个(库存、支付、物流、通知) | 多个 JDBC 类(Connection、Statement、ResultSet) |
| Facade 职责 | 封装固定调用顺序 | 封装业务流程 + 异常回滚 | 封装复杂 API,简化调用 |
| 子系统创建方式 | 外部注入(构造器参数) | Facade 内部创建 | Facade 内部创建 |
| 是否有返回值 | 无(void) | 有(boolean 成功/失败) | 有(ResultSet、int) |
| 异常处理 | 无 | 有(回滚库存、退款) | 有(事务回滚) |
| Client 复杂度 | 极低(只需 1 个方法) | 极低(只需 1 个方法) | 极低(只需 1 个方法) |
| 软考考法 | 下午大题(补全调用顺序) | 下午大题(补全业务流程) | 上午选择题(识别简化 API) |
七、外观模式 vs 适配器模式 vs 代理模式:核心对比
| 对比项 | 外观模式 | 适配器模式 | 代理模式 |
|---|---|---|---|
| 设计目的 | 简化 子系统的使用,提供高层接口 | 转换 接口,兼容不兼容的类 | 控制 对象的访问(延迟加载、权限等) |
| 接口变化 | 提供 新的高层 接口(封装多个子接口) | 转换 已有接口为另一个接口 | 保持 原接口不变 |
| 子系统数量 | 通常涉及 多个 子系统类 | 通常涉及 一个 Adaptee | 通常涉及 一个 RealSubject |
| Client 调用 | 只调用 Facade | 调用 Adapter(Adapter 内部调用 Adaptee) | 调用 Proxy(Proxy 内部调用 RealSubject) |
| 是否隐藏子系统 | 部分隐藏(Client 可以选择直接访问子系统) | 完全隐藏(Client 不知道 Adaptee) | 完全透明(Client 以为在调用 RealSubject) |
| 软考判断 | 一个类封装多个子系统的调用 | 一个类转换两个不同接口 | 一个类控制对另一个类的访问 |
记忆口诀:外观是 "简化入口"(封装复杂子系统),适配器是 "翻译官"(接口转换),代理是 "中间人"(控制访问)。
八、软考高频考点与易混淆辨析
8.1 高频考点
| 考点 | 内容 |
|---|---|
| 模式分类 | 结构型模式(GoF 23 正式成员) |
| 核心角色 | Facade(外观)、Subsystem(子系统,多个)、Client(客户端) |
| 核心思想 | 为子系统提供 统一的高层接口,降低客户端与子系统的耦合 |
| 设计原则 | 最少知识原则(迪米特法则):客户端只需与 Facade 交互,不需要了解子系统内部 |
| 是否阻止直接访问 | ❌ 不阻止。外观只是提供一个简化入口,客户端仍可直接访问子系统 |
| 与单例结合 | 外观类通常设计为 单例(整个系统只需要一个门面) |
| 适用场景 | ① 子系统复杂,客户端调用成本高 ② 需要分层架构(层与层之间通过外观交互)③ 需要解耦客户端与子系统 |
| 与中介者区别 | 外观:Client → Facade → Subsystem(单向,Facade 封装子系统);中介者:Colleague ↔ Mediator ↔ Colleague(双向,协调多个对等对象) |
8.2 易混淆辨析:外观 vs 中介者
| 对比项 | 外观模式 | 中介者模式 |
|---|---|---|
| 交互方向 | 单向:Client → Facade → Subsystem | 双向:Colleague ↔ Mediator ↔ Colleague |
| 对象关系 | Facade 封装 子系统(子系统不知道 Facade 的存在) | Mediator 协调 对等对象(同事对象知道 Mediator 的存在) |
| 目的 | 简化客户端对子系统的使用 | 解耦多个对象之间的相互引用 |
| 类图结构 | Facade 依赖多个 Subsystem | Mediator 与多个 Colleague 双向关联 |
| 软考判断 | 一个类封装多个子系统供客户端使用 | 一个类协调多个对等对象之间的交互 |
九、真题风格模拟与代码填空
模拟题 1(上午选择题)
以下关于外观模式的叙述中,正确的是()。
A. 外观模式属于创建型模式,主要用于创建子系统的统一入口
B. 在外观模式中,Facade 类通过继承子系统类来简化客户端调用
C. 外观模式为子系统中的一组接口提供一个统一的高层接口,使得子系统更加容易使用
D. 外观模式会阻止客户端直接访问子系统的内部类,强制所有调用必须通过 Facade
答案:C
解析:
-
A 错误:外观属于 结构型 模式,不是创建型。
-
B 错误:Facade 通过 组合(持有子系统引用)来调用子系统,不是继承。外观模式不修改子系统的继承结构。
-
C 正确:这是外观模式的核心定义。
-
D 错误:外观模式 不阻止 客户端直接访问子系统。Facade 只是提供一个简化入口,如果客户端需要更细粒度的控制,仍然可以直接调用子系统类。
模拟题 2(下午代码填空 --- 补全外观类封装逻辑)
某系统使用外观模式封装编译子系统。编译流程包括:词法分析(Lexer)、语法分析(Parser)、代码生成(CodeGenerator)。请补全(1)~(4)。
java
// Subsystem:词法分析
class Lexer {
public String tokenize(String source) {
System.out.println("词法分析完成");
return "tokens";
}
}
// Subsystem:语法分析
class Parser {
public String parse(String tokens) {
System.out.println("语法分析完成");
return "ast";
}
}
// Subsystem:代码生成
class CodeGenerator {
public String generate(String ast) {
System.out.println("代码生成完成");
return "machine_code";
}
}
// Facade:编译外观
class CompilerFacade {
private Lexer lexer;
private Parser parser;
private CodeGenerator generator;
public CompilerFacade() {
this.lexer = new Lexer();
this.parser = new Parser();
this.generator = new CodeGenerator();
}
// 一键编译
public String compile(String sourceCode) {
String tokens = (1)______;
String ast = (2)______;
String code = (3)______;
return (4)______;
}
}
public class Client {
public static void main(String[] args) {
CompilerFacade compiler = new CompilerFacade();
String result = compiler.compile("int main() { return 0; }");
System.out.println("编译结果: " + result);
}
}
答案:
-
(1)
lexer.tokenize(sourceCode) -
(2)
parser.parse(tokens) -
(3)
generator.generate(ast) -
(4)
code
阅卷要点:
(1)(2)(3) 必须体现 Facade 对子系统方法的 顺序调用。顺序不能错(先词法、再语法、再生成),但方法名必须准确。
(4) 必须是
code(或generator.generate(ast)),返回最终的编译结果。如果写tokens或ast会扣分,因为那不是最终产物。注意:Facade 内部的方法调用是通过 组合的成员变量 (
lexer、parser、generator)调用,不是通过类名直接调用静态方法。
模拟题 3(下午代码填空 --- 外观类与单例结合)
某系统的外观类
ServiceFacade需要设计为单例,封装UserService和OrderService两个子系统。请补全(1)~(3)。
java
class UserService {
public String getUser(String id) { return "User:" + id; }
}
class OrderService {
public String getOrder(String userId) { return "Order of " + userId; }
}
class ServiceFacade {
private static final ServiceFacade (1)______ = new ServiceFacade();
private UserService userService;
private OrderService orderService;
private ServiceFacade() {
this.userService = new UserService();
this.orderService = new OrderService();
}
public static ServiceFacade (2)______() {
return (3)______;
}
public String getUserOrderInfo(String userId) {
String user = userService.getUser(userId);
String order = orderService.getOrder(userId);
return user + " -> " + order;
}
}
public class Client {
public static void main(String[] args) {
String info = ServiceFacade.getInstance().getUserOrderInfo("1001");
System.out.println(info);
}
}
答案:
-
(1)
INSTANCE -
(2)
getInstance -
(3)
INSTANCE
阅卷要点:
(1) 必须是
INSTANCE(或instance),这是单例的标准命名。static final修饰表示饿汉式单例。(2) 必须是
getInstance,这是单例的标准获取方法名。(3) 必须是
INSTANCE,返回单例实例。不能写new ServiceFacade()(构造器是 private)。此题结合了 单例模式 和 外观模式,是软考下午题中常见的组合考法。
十、常见陷阱与注意事项
陷阱 1:误认为外观是创建型模式
外观属于 结构型 模式,因为它解决的是 类与类之间的结构关系 (Facade 与子系统的组合),而不是对象的创建问题。软考上午题如果问"以下属于创建型模式的是",选项里出现"外观" 不能选。
陷阱 2:Facade 继承子系统类
这是软考 常见错误写法 。Facade 应该通过 组合 (持有子系统引用)来使用子系统,而不是继承子系统。如果 Facade 继承了子系统类,那就破坏了子系统的独立性,也违背了外观模式的初衷。软考代码填空里,Facade 与子系统之间是 关联/依赖关系(Facade 类里有子系统的成员变量),不是继承关系。
陷阱 3:认为外观阻止了直接访问子系统
外观模式 不阻止 客户端直接访问子系统。Facade 只是提供了一个 简化的入口 ,如果客户端需要更细粒度的控制,仍然可以直接调用子系统类。如果题干说"外观模式强制所有调用必须通过 Facade",那是 错误 的。
陷阱 4:与适配器模式混淆
表格
| 外观模式 | 适配器模式 |
|---|---|
| 封装 多个 子系统的复杂调用 | 转换 一个 类的接口 |
| 提供 新的高层 接口 | 将 已有接口 转为另一个接口 |
| 目的是 简化使用 | 目的是 兼容接口 |
如果类图里 Facade 连接多个子系统,且 Client 只连 Facade → 外观。如果 Adapter 连接 Target 和 Adaptee → 适配器。
陷阱 5:与代理模式混淆
| 外观模式 | 代理模式 |
|---|---|
| 封装 多个 子系统 | 控制 一个 对象的访问 |
| Client 知道自己在调用 Facade | Client 通常不知道自己在调用 Proxy |
| 目的是 简化 | 目的是 控制 |
陷阱 6:与中介者模式混淆
这是软考 最难的辨析题。
-
外观 :Client → Facade → Subsystem(单向,Facade 封装子系统供客户端使用)。
-
中介者 :Colleague ↔ Mediator ↔ Colleague(双向,Mediator 协调多个对等对象之间的交互)。
快速判断:如果类图里有一个中心类,多个子系统类只被中心类调用,且客户端只与中心类交互 → 外观。如果多个对等对象都通过中心类互相通信 → 中介者。
陷阱 7:Facade 的构造器参数
Facade 的构造器有两种设计方式:
-
内部创建 :在 Facade 构造器里
new Subsystem()(如场景二)。 -
外部注入:通过构造器参数传入子系统实例(如场景一)。
两种方式都算对,但软考代码填空时要注意:如果题干说 "Facade 自行管理子系统生命周期",则内部创建;如果题干说 "子系统由外部配置",则外部注入。
十一、总结
| 要点 | 内容 |
|---|---|
| 定义 | 为子系统中的一组接口提供一个统一的高层接口,使得子系统更加容易使用 |
| 分类 | 结构型模式(GoF 23 正式成员) |
| 核心角色 | Facade(外观)、Subsystem(子系统,多个)、Client(客户端) |
| 核心思想 | 封装子系统的复杂调用逻辑,提供一个简洁的高层入口 |
| 设计原则 | 最少知识原则(迪米特法则):客户端只需与 Facade 交互 |
| 是否阻止直接访问 | ❌ 不阻止。Facade 只是简化入口,客户端仍可直接访问子系统 |
| 与单例结合 | 外观类通常设计为单例(整个系统只需要一个门面) |
| 与适配器区别 | 外观:简化多个子系统的使用;适配器:转换一个类的接口 |
| 与代理区别 | 外观:简化多个子系统;代理:控制一个对象的访问 |
| 与中介者区别 | 外观:单向封装子系统;中介者:双向协调对等对象 |
| 软考重点 | 代码填空(补全 Facade 对子系统的顺序调用);类图识别(Facade 连接多个 Subsystem);与中介者的辨析 |
| 答题技巧 | 看到 "封装复杂子系统"、"提供统一入口"、"简化调用" → 外观;看到 Facade 类封装多个子系统方法 → 确认外观 |
系列预告 :下一篇将讲 享元模式 ------ 当系统中存在大量相似对象、内存开销巨大时,如何通过共享技术有效地支持大量细粒度的对象。咱们下回见。