教程中心 / Java教程 / 外观模式

外观模式

10分钟 设计模式

什么是外观模式?

外观模式(Facade Pattern)是一种结构型设计模式,它为复杂的子系统提供一个统一的接口,让客户端可以更简单地使用子系统。外观模式隐藏了系统的复杂性,提供了一个客户端可以轻松访问的接口。

生活比喻

想象你买电脑的过程。如果你自己去组装,需要分别联系CPU厂商、内存厂商、主板厂商、显卡厂商...这太复杂了!但如果你去电脑城找一家"装机店",他们帮你搞定一切。你只需要告诉他们你的需求和预算。装机店就是"外观",它封装了所有子系统的复杂性,对你隐藏了细节。

外观模式结构:

没有外观模式(客户端直接调用子系统):
┌──────────┐     ┌─────────┐
│ 客户端A  │ ──► │ 子系统1 │ ← CPU
│ 客户端B  │ ──► │ 子系统2 │ ← 主板
│ 客户端C  │ ──► │ 子系统3 │ ← 内存
└──────────┘     │ 子系统4 │ ← 硬盘
                 │   ...   │
                 └─────────┘

有外观模式(客户端通过外观调用子系统):
┌──────────┐
│  客户端   │      ┌─────────────┐      ┌─────────┐
│          │ ───► │   Facade    │ ───► │ 子系统1 │
│          │      │  (外观类)   │ ───► │ 子系统2 │
│          │      │             │ ───► │ 子系统3 │
└──────────┘      └─────────────┘      └─────────┘

为什么需要外观模式?

没有外观模式的问题

// 客户端需要知道所有子系统的细节
public class WithoutFacade {
    public static void main(String[] args) {
        // 启动电脑,需要调用很多子系统
        CPU cpu = new CPU();
        Memory memory = new Memory();
        HardDisk disk = new HardDisk();
        GraphicsCard gpu = new GraphicsCard();
        Monitor monitor = new Monitor();

        // 客户端需要知道每个组件的启动顺序和细节
        cpu.start();
        memory.load();
        disk.read();
        gpu.initialize();
        monitor.turnOn();
        // 太复杂了!客户端不应该知道这些细节
    }
}

完整实现

1. 定义子系统类

// 子系统A - CPU
class CPU {
    public void start() {
        System.out.println("  [CPU] 启动...");
        System.out.println("  [CPU] 执行指令...");
    }

    public void shutdown() {
        System.out.println("  [CPU] 关闭...");
    }
}

// 子系统B - 内存
class Memory {
    public void load() {
        System.out.println("  [Memory] 加载数据到内存...");
    }

    public void refresh() {
        System.out.println("  [Memory] 刷新内存...");
    }
}

// 子系统C - 硬盘
class HardDisk {
    public void read() {
        System.out.println("  [HardDisk] 从硬盘读取数据...");
    }

    public void write() {
        System.out.println("  [HardDisk] 写入数据到硬盘...");
    }
}

// 子系统D - 显卡
class GraphicsCard {
    public void initialize() {
        System.out.println("  [GPU] 初始化显卡驱动...");
    }

    public void display() {
        System.out.println("  [GPU] 显示画面...");
    }
}

// 子系统E - 显示器
class Monitor {
    public void turnOn() {
        System.out.println("  [Monitor] 显示器开机...");
    }

    public void turnOff() {
        System.out.println("  [Monitor] 显示器关机...");
    }
}

// 子系统F - 网络
class NetworkCard {
    public void connect() {
        System.out.println("  [Network] 连接网络...");
    }

    public void disconnect() {
        System.out.println("  [Network] 断开网络连接...");
    }
}

2. 创建外观类

// 外观类 - 提供统一的简化接口
class ComputerFacade {
    // 持有所有子系统
    private CPU cpu;
    private Memory memory;
    private HardDisk hardDisk;
    private GraphicsCard graphicsCard;
    private Monitor monitor;
    private NetworkCard networkCard;

    public ComputerFacade() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDisk = new HardDisk();
        this.graphicsCard = new GraphicsCard();
        this.monitor = new Monitor();
        this.networkCard = new NetworkCard();
    }

    // 统一的开机方法 - 封装了所有细节
    public void startComputer() {
        System.out.println("====== 开机流程开始 ======");
        cpu.start();
        memory.load();
        hardDisk.read();
        graphicsCard.initialize();
        monitor.turnOn();
        networkCard.connect();
        System.out.println("====== 开机完成! ======\n");
    }

    // 统一的关机方法
    public void shutdownComputer() {
        System.out.println("====== 关机流程开始 ======");
        networkCard.disconnect();
        monitor.turnOff();
        memory.refresh();
        hardDisk.write();
        cpu.shutdown();
        System.out.println("====== 关机完成 ======\n");
    }

    // 玩游戏模式 - 针对特定场景的便捷方法
    public void playGame() {
        System.out.println("====== 启动游戏模式 ======");
        cpu.start();
        memory.load();
        graphicsCard.initialize();
        monitor.turnOn();
        networkCard.connect();
        System.out.println("====== 游戏已就绪! ======\n");
    }
}

3. 客户端使用

public class FacadeDemo {
    public static void main(String[] args) {
        // 创建外观
        ComputerFacade computer = new ComputerFacade();

        System.out.println("===== 场景1:日常办公 =====");
        computer.startComputer();

        System.out.println("===== 场景2:玩游戏 =====");
        computer.playGame();

        System.out.println("===== 场景3:关机 =====");
        computer.shutdownComputer();
    }
}

实战案例:用户注册系统

在用户注册系统中,外观模式可以封装邮箱服务、短信服务、积分服务等:

// 子系统:邮件服务
class EmailService {
    public void sendWelcomeEmail(String email) {
        System.out.println("[Email] 发送欢迎邮件到: " + email);
    }
}

// 子系统:短信服务
class SMSService {
    public void sendVerifyCode(String phone) {
        System.out.println("[SMS] 发送验证码到: " + phone);
    }
}

// 子系统:积分服务
class PointService {
    public void addPoints(String userId, int points) {
        System.out.println("[Points] 给用户 " + userId + " 增加 " + points + " 积分");
    }
}

// 子系统:日志服务
class LogService {
    public void log(String message) {
        System.out.println("[Log] " + message);
    }
}

// 外观:用户注册服务
class UserRegisterFacade {
    private EmailService emailService;
    private SMSService smsService;
    private PointService pointService;
    private LogService logService;

    public UserRegisterFacade() {
        this.emailService = new EmailService();
        this.smsService = new SMSService();
        this.pointService = new PointService();
        this.logService = new LogService();
    }

    // 统一的注册方法
    public void register(String username, String email, String phone) {
        System.out.println("\n==== 用户注册流程 ====");

        // 1. 保存用户到数据库(假设)
        logService.log("保存用户: " + username);
        String userId = "U" + System.currentTimeMillis();

        // 2. 发送欢迎邮件
        emailService.sendWelcomeEmail(email);

        // 3. 发送验证码
        smsService.sendVerifyCode(phone);

        // 4. 赠送注册积分
        pointService.addPoints(userId, 100);

        logService.log("用户注册成功: " + userId);
        System.out.println("==== 注册完成 ====\n");
    }
}

// 使用
public class UserRegisterDemo {
    public static void main(String[] args) {
        UserRegisterFacade registerService = new UserRegisterFacade();
        registerService.register("张三", "zhang@example.com", "13800138000");
    }
}

外观模式的优缺点

优点

简化接口:客户端不需要了解子系统细节
解耦:客户端与子系统解耦
易用:降低学习成本

缺点

过度封装:可能隐藏了客户端需要的功能
违反开闭:添加新子系统可能需要修改外观类

外观模式 vs 其他模式

模式目的关注点
外观模式简化接口封装子系统,提供统一入口
适配器模式接口转换将不兼容接口转换为可兼容
装饰器模式动态增强为对象添加新功能
观察者模式通知机制一对多依赖,状态变化自动通知

应用场景

1

复杂类库/框架封装

MyBatis的SqlSession封装了JDBC细节

2

企业应用集成

ESB(企业服务总线)作为外观

3

第三方SDK封装

封装微信支付、支付宝等复杂API

小结

  • 外观模式为复杂子系统提供统一接口,简化客户端调用
  • 外观类持有所有子系统实例,封装调用顺序和逻辑
  • 客户端通过外观访问子系统,无需了解内部细节
  • 符合迪米特法则(最少知道原则)
  • 常用于封装复杂类库、框架、第三方SDK
☕ Java 在线代码编辑器
📝 运行结果