外观模式 (Facade Pattern)
为子系统中的一组接口提供一个一致的界面
模式定义
外观模式是一种结构型设计模式,它为子系统中的一组接口提供一个一致的界面。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式通过创建一个包装类来包裹复杂的系统,使客户端可以通过这个包装类来访问系统。
模式结构
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();
}
}
}
适用场景
- 为复杂的子系统提供简单的接口
- 客户程序与抽象类的实现部分之间存在很大的依赖性
- 需要构建多层结构的系统
- 当子系统变得越来越复杂时,使用外观模式可以提供一个简单的接口,隐藏子系统的复杂性
- 引入外观模式将这个子系统与客户以及其他子系统分离,可以提高子系统的独立性和可移植性
- 利用外观模式开发每层的入口,可以简化层间调用,降低层间耦合
优缺点
优点
- 简化客户端使用:客户端不需要了解子系统的复杂性,只需要通过外观类来访问系统
- 松散耦合:外观模式实现了子系统与客户端之间的松散耦合
- 更好的层次结构:外观模式有助于建立层次结构的系统
- 符合迪米特法则:客户端不需要知道子系统的内部细节
- 提高安全性:通过外观类可以控制客户端对子系统的访问
缺点
- 不符合开闭原则:如果需要增加或修改子系统功能,可能需要修改外观类
- 增加新的子系统可能需要修改外观类:这违背了开闭原则
- 可能会限制系统功能:外观类可能屏蔽了子系统的有用功能
- 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类