模式定义

外观模式是一种结构型设计模式,它为子系统中的一组接口提供一个一致的界面。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式通过创建一个包装类来包裹复杂的系统,使客户端可以通过这个包装类来访问系统。

模式结构

graph TD A[Client] --> B((Facade)) B --> C[SubsystemA] B --> D[SubsystemB] B --> E[SubsystemC]

实现方式

基础实现

定义子系统类和外观类,外观类封装子系统的复杂操作。

// 子系统A - CPU
public class CPU {
    public void freeze() {
        System.out.println("CPU: 冻结");
    }
    
    public void jump(long position) {
        System.out.println("CPU: 跳转到位置 " + position);
    }
    
    public void execute() {
        System.out.println("CPU: 执行指令");
    }
}

// 子系统B - 内存
public class Memory {
    public void load(long position, byte[] data) {
        System.out.println("内存: 在位置 " + position + " 加载数据");
    }
}

// 子系统C - 硬盘
public class HardDrive {
    public byte[] read(long lba, int size) {
        System.out.println("硬盘: 读取扇区 " + lba + " 大小 " + size);
        return new byte[size];
    }
}

// 外观类 - 计算机
public class Computer {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;
    
    public Computer() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
    }
    
    public void start() {
        System.out.println("计算机启动过程:");
        cpu.freeze();
        memory.load(0, hardDrive.read(0, 1024));
        cpu.jump(0);
        cpu.execute();
        System.out.println("计算机启动完成!");
    }
    
    public void shutdown() {
        System.out.println("计算机关闭过程:");
        cpu.freeze();
        System.out.println("计算机关闭完成!");
    }
}

使用示例

public class FacadePatternDemo {
    public static void main(String[] args) {
        Computer computer = new Computer();
        
        // 客户端只需要调用外观类的方法,而不需要了解子系统的复杂性
        computer.start();
        System.out.println();
        computer.shutdown();
    }
}

家庭影院系统示例

使用外观模式实现家庭影院系统。

// 子系统 - 投影仪
public class Projector {
    public void on() {
        System.out.println("投影仪: 打开");
    }
    
    public void off() {
        System.out.println("投影仪: 关闭");
    }
    
    public void wideScreenMode() {
        System.out.println("投影仪: 宽屏模式");
    }
}

// 子系统 - 功放
public class Amplifier {
    public void on() {
        System.out.println("功放: 打开");
    }
    
    public void off() {
        System.out.println("功放: 关闭");
    }
    
    public void setDvd(DVDPlayer dvd) {
        System.out.println("功放: 设置DVD播放器");
    }
    
    public void setSurroundSound() {
        System.out.println("功放: 设置环绕声");
    }
    
    public void setVolume(int level) {
        System.out.println("功放: 音量设置为 " + level);
    }
}

// 子系统 - DVD播放器
public class DVDPlayer {
    public void on() {
        System.out.println("DVD播放器: 打开");
    }
    
    public void off() {
        System.out.println("DVD播放器: 关闭");
    }
    
    public void play(String movie) {
        System.out.println("DVD播放器: 播放电影 \"" + movie + "\"");
    }
    
    public void stop() {
        System.out.println("DVD播放器: 停止播放");
    }
    
    public void eject() {
        System.out.println("DVD播放器: 弹出DVD");
    }
}

// 子系统 - 屏幕
public class Screen {
    public void up() {
        System.out.println("屏幕: 上升");
    }
    
    public void down() {
        System.out.println("屏幕: 下降");
    }
}

// 子系统 - 灯光
public class TheaterLights {
    public void dim(int level) {
        System.out.println("灯光: 调暗到 " + level + "%");
    }
    
    public void on() {
        System.out.println("灯光: 打开");
    }
    
    public void off() {
        System.out.println("灯光: 关闭");
    }
}

// 子系统 - 爆米花机
public class PopcornPopper {
    public void on() {
        System.out.println("爆米花机: 打开");
    }
    
    public void off() {
        System.out.println("爆米花机: 关闭");
    }
    
    public void pop() {
        System.out.println("爆米花机: 制作爆米花");
    }
}

// 外观类 - 家庭影院外观
public class HomeTheaterFacade {
    private Amplifier amp;
    private Tuner tuner;
    private DVDPlayer dvd;
    private Projector projector;
    private Screen screen;
    private TheaterLights lights;
    private PopcornPopper popper;
    
    public HomeTheaterFacade(Amplifier amp, Tuner tuner, DVDPlayer dvd,
                            Projector projector, Screen screen, TheaterLights lights,
                            PopcornPopper popper) {
        this.amp = amp;
        this.tuner = tuner;
        this.dvd = dvd;
        this.projector = projector;
        this.screen = screen;
        this.lights = lights;
        this.popper = popper;
    }
    
    public void watchMovie(String movie) {
        System.out.println("准备观看电影 \"" + movie + "\"...");
        popper.on();
        popper.pop();
        lights.dim(10);
        screen.down();
        projector.on();
        projector.wideScreenMode();
        amp.on();
        amp.setDvd(dvd);
        amp.setSurroundSound();
        amp.setVolume(5);
        dvd.on();
        dvd.play(movie);
    }
    
    public void endMovie() {
        System.out.println("结束观看电影...");
        popper.off();
        lights.on();
        screen.up();
        projector.off();
        amp.off();
        dvd.stop();
        dvd.eject();
        dvd.off();
    }
    
    public void listenToRadio(double frequency) {
        System.out.println("收听广播...");
        tuner.on();
        tuner.setFrequency(frequency);
        amp.on();
        amp.setVolume(5);
    }
    
    public void endRadio() {
        System.out.println("关闭广播...");
        tuner.off();
        amp.off();
    }
}

// 客户端使用
public class HomeTheaterTestDrive {
    public static void main(String[] args) {
        // 创建子系统组件
        Amplifier amp = new Amplifier();
        Tuner tuner = new Tuner();
        DVDPlayer dvd = new DVDPlayer();
        Projector projector = new Projector();
        Screen screen = new Screen();
        TheaterLights lights = new TheaterLights();
        PopcornPopper popper = new PopcornPopper();
        
        // 创建外观
        HomeTheaterFacade homeTheater = new HomeTheaterFacade(amp, tuner, dvd, projector, screen, lights, popper);
        
        // 观看电影
        homeTheater.watchMovie("《头号玩家》");
        System.out.println("\n电影结束...\n");
        homeTheater.endMovie();
    }
}

经典案例

Spring Framework 中的外观模式

Spring 框架中的 ApplicationContext 是外观模式的典型应用。

// Spring 中的 ApplicationContext 作为外观模式示例
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

// 假设我们有以下服务类
public class UserService {
    public void saveUser() {
        System.out.println("保存用户信息");
    }
}

public class OrderService {
    public void saveOrder() {
        System.out.println("保存订单信息");
    }
}

public class PaymentService {
    public void processPayment() {
        System.out.println("处理支付");
    }
}

// 外观类 - 业务外观
public class BusinessFacade {
    private UserService userService;
    private OrderService orderService;
    private PaymentService paymentService;
    
    public BusinessFacade() {
        // 在实际Spring应用中,这些服务会通过依赖注入获得
        this.userService = new UserService();
        this.orderService = new OrderService();
        this.paymentService = new PaymentService();
    }
    
    // 外观方法 - 封装复杂的业务流程
    public void processOrder(String userName, String productName, double amount) {
        System.out.println("开始处理订单流程...");
        userService.saveUser();
        orderService.saveOrder();
        paymentService.processPayment();
        System.out.println("订单处理完成!");
    }
}

// 客户端使用
public class SpringFacadeExample {
    public static void main(String[] args) {
        // 使用外观模式简化复杂操作
        BusinessFacade businessFacade = new BusinessFacade();
        businessFacade.processOrder("张三", "iPhone 15", 9999.0);
        
        // 如果不使用外观模式,客户端需要直接与多个子系统交互
        System.out.println("\n不使用外观模式的复杂调用:");
        UserService userService = new UserService();
        OrderService orderService = new OrderService();
        PaymentService paymentService = new PaymentService();
        
        userService.saveUser();
        orderService.saveOrder();
        paymentService.processPayment();
    }
}

Java 标准库中的外观模式

Java 标准库中也有很多外观模式的应用。

// URL 类作为外观模式示例
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class URLFacadeExample {
    public static void main(String[] args) {
        try {
            // URL 类作为外观,隐藏了网络通信的复杂性
            URL url = new URL("https://www.baidu.com");
            
            // 客户端只需要调用 URL 的方法,而不需要了解底层的网络协议实现
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            
            // 设置请求方法
            connection.setRequestMethod("GET");
            
            // 读取响应
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
                break; // 只打印第一行
            }
            reader.close();
            
            connection.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

适用场景

优缺点

优点

  • 简化客户端使用:客户端不需要了解子系统的复杂性,只需要通过外观类来访问系统
  • 松散耦合:外观模式实现了子系统与客户端之间的松散耦合
  • 更好的层次结构:外观模式有助于建立层次结构的系统
  • 符合迪米特法则:客户端不需要知道子系统的内部细节
  • 提高安全性:通过外观类可以控制客户端对子系统的访问

缺点

  • 不符合开闭原则:如果需要增加或修改子系统功能,可能需要修改外观类
  • 增加新的子系统可能需要修改外观类:这违背了开闭原则
  • 可能会限制系统功能:外观类可能屏蔽了子系统的有用功能
  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类