0119_外观模式(Facade)

外观模式(Facade)

意图

为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

UML 图

Facade

优点

  1. 简化客户端使用:客户端不需要了解系统内部的复杂实现,只需要与外观对象交互
  2. 降低耦合度:将客户端与子系统解耦,使得子系统的变化不会影响到客户端
  3. 提高可维护性:将复杂的子系统调用封装起来,便于维护和修改
  4. 符合迪米特法则:客户端只与外观对象交互,减少了与其他对象的依赖
  5. 易于使用:提供了一个简单的接口,隐藏了系统的复杂性

缺点

  1. 不符合开闭原则:如果需要添加新的功能,通常需要修改外观类
  2. 可能成为上帝对象:如果过度使用,外观类可能变得过于庞大和复杂
  3. 限制了灵活性:客户端无法直接访问子系统的特定功能
  4. 可能产生性能问题:额外的调用层可能带来轻微的性能开销

代码示例

以上班打开电脑、工作运行电脑、下班关闭电脑为例,电脑包含多个子系统(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标准库和框架中有广泛应用:

  1. JDBC数据库连接

    // DriverManager作为外观,隐藏了数据库连接的复杂细节
    Connection connection = DriverManager.getConnection(url, username, password);
    Statement statement = connection.createStatement();
    ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
    
  2. Spring框架

    // Spring的JdbcTemplate作为外观,简化了JDBC操作
    jdbcTemplate.query("SELECT * FROM users", new RowMapper<User>() {
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setName(rs.getString("name"));
            return user;
        }
    });
    
  3. Servlet API

    // HttpServletRequest作为外观,提供了访问HTTP请求信息的统一接口
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    String method = request.getMethod();
    
  4. Java日志框架

    // SLF4J作为外观,提供了统一的日志接口
    Logger logger = LoggerFactory.getLogger(MyClass.class);
    logger.info("This is an info message");
    logger.error("This is an error message");
    

总结

外观模式通过提供一个统一的接口来简化复杂系统的使用,它将客户端与子系统的复杂性隔离开来。这种模式特别适用于:

  • 需要为复杂子系统提供简单入口点的场景
  • 客户端不需要直接访问子系统所有功能的情况
  • 想要降低系统耦合度,提高可维护性的场景

外观模式不是要封装所有子系统功能,而是提供一组常用的功能,让客户端更容易使用系统。对于需要访问特定高级功能的客户端,仍然可以直接访问子系统组件。

posted @ 2025-09-03 08:02  庞去广  阅读(10)  评论(0)    收藏  举报