外观模式(Facade)
意图
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
UML 图
优点
- 简化客户端使用:客户端不需要了解系统内部的复杂实现,只需要与外观对象交互
- 降低耦合度:将客户端与子系统解耦,使得子系统的变化不会影响到客户端
- 提高可维护性:将复杂的子系统调用封装起来,便于维护和修改
- 符合迪米特法则:客户端只与外观对象交互,减少了与其他对象的依赖
- 易于使用:提供了一个简单的接口,隐藏了系统的复杂性
缺点
- 不符合开闭原则:如果需要添加新的功能,通常需要修改外观类
- 可能成为上帝对象:如果过度使用,外观类可能变得过于庞大和复杂
- 限制了灵活性:客户端无法直接访问子系统的特定功能
- 可能产生性能问题:额外的调用层可能带来轻微的性能开销
代码示例
以上班打开电脑、工作运行电脑、下班关闭电脑为例,电脑包含多个子系统(CPU、硬盘、内存等)。使用外观模式提供一个统一的控制接口:
1. 子系统类 (Subsystem Classes)
// CPU
public class CPU {public void start() {System.out.println("CPU启动中...");System.out.println("CPU初始化完成");}public void shutdown() {System.out.println("CPU关闭中...");System.out.println("CPU已关闭");}public void execute() {System.out.println("CPU执行指令");}
}// 硬盘子系统
public class HardDrive {public void start() {System.out.println("硬盘启动中...");System.out.println("硬盘初始化完成");}public void shutdown() {System.out.println("硬盘关闭中...");System.out.println("硬盘已关闭");}public void read() {System.out.println("硬盘读取数据");}public void write() {System.out.println("硬盘写入数据");}
}// 内存子系统
public class Memory {public void start() {System.out.println("内存启动中...");System.out.println("内存初始化完成");}public void shutdown() {System.out.println("内存关闭中...");System.out.println("内存已关闭");}public void load() {System.out.println("内存加载数据");}
}
2. 外观类 (Facade Class)
// 电脑外观类
public class ComputerFacade {private CPU cpu;private Memory memory;private HardDrive hardDrive;public ComputerFacade() {this.cpu = new CPU();this.memory = new Memory();this.hardDrive = new HardDrive();}/*** 开机操作* 按照正确的顺序启动各个子系统*/public void start() {System.out.println("======= 开始启动电脑 =======");cpu.start();memory.start();hardDrive.start();System.out.println("======= 电脑启动完成 =======\n");}/*** 关机操作* 按照正确的顺序关闭各个子系统*/public void shutdown() {System.out.println("======= 开始关闭电脑 =======");hardDrive.shutdown();memory.shutdown();cpu.shutdown();System.out.println("======= 电脑关闭完成 =======\n");}/*** 工作操作* 演示电脑正常工作时各组件的协作*/public void work() {System.out.println("======= 电脑工作中 =======");cpu.execute();memory.load();hardDrive.read();hardDrive.write();System.out.println("======= 工作完成 =======\n");}
}
3. 客户端代码 (Client Code)
public class ComputerFacadeTest {public static void main(String[] args) {// 创建电脑外观对象ComputerFacade computer = new ComputerFacade();// 上班 - 开机System.out.println("---------- 上班时间 ----------");computer.start();// 工作System.out.println("---------- 开始工作 ----------");computer.work();// 下班 - 关机System.out.println("---------- 下班时间 ----------");computer.shutdown();}
}
在Java标准库中的应用
外观模式在Java标准库和框架中有广泛应用:
-
JDBC数据库连接
// DriverManager作为外观,隐藏了数据库连接的复杂细节 Connection connection = DriverManager.getConnection(url, username, password); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
-
Spring框架
// Spring的JdbcTemplate作为外观,简化了JDBC操作 jdbcTemplate.query("SELECT * FROM users", new RowMapper<User>() {@Overridepublic User mapRow(ResultSet rs, int rowNum) throws SQLException {User user = new User();user.setId(rs.getInt("id"));user.setName(rs.getString("name"));return user;} });
-
Servlet API
// HttpServletRequest作为外观,提供了访问HTTP请求信息的统一接口 String username = request.getParameter("username"); String password = request.getParameter("password"); String method = request.getMethod();
-
Java日志框架
// SLF4J作为外观,提供了统一的日志接口 Logger logger = LoggerFactory.getLogger(MyClass.class); logger.info("This is an info message"); logger.error("This is an error message");
总结
外观模式通过提供一个统一的接口来简化复杂系统的使用,它将客户端与子系统的复杂性隔离开来。这种模式特别适用于:
- 需要为复杂子系统提供简单入口点的场景
- 客户端不需要直接访问子系统所有功能的情况
- 想要降低系统耦合度,提高可维护性的场景
外观模式不是要封装所有子系统功能,而是提供一组常用的功能,让客户端更容易使用系统。对于需要访问特定高级功能的客户端,仍然可以直接访问子系统组件。