0122_享元模式(Flyweight)

享元模式(Flyweight)

意图

运用共享技术有效地支持大量细粒度的对象,通过共享相似对象来减少内存使用和提高性能。

UML 图

Flyweight

优点

  1. 减少内存使用:通过共享相似对象,显著减少内存占用
  2. 提高性能:减少了对象创建和垃圾回收的开销
  3. 简化对象管理:集中管理共享对象,便于维护和扩展
  4. 支持大量对象:能够有效处理大量细粒度对象的情况
  5. 分离内在和外在状态:将不变的内在状态与变化的外在状态分离

缺点

  1. 增加系统复杂度:需要区分内在状态和外在状态,增加了设计复杂性
  2. 线程安全问题:共享对象需要保证线程安全,可能需要同步机制
  3. 可能影响性能:如果外在状态计算复杂,可能会抵消共享带来的性能优势
  4. 不适用于所有场景:只有当对象具有大量相似状态时才适用

代码示例

以机器人制造为例,不同类型的机器人共享相同的内部状态(型号、基础配置),而外部状态(位置、任务)各不相同:

1. 享元接口 (Flyweight Interface)

// 机器人接口 - 享元
public interface Robot {
    void operate(String location, String task);
    String getModel();
}

2. 具体享元 (Concrete Flyweight)

// 具体机器人类型 - 具体享元
public class ConcreteRobot implements Robot {
    private String model;  // 内在状态(共享)
    private String baseConfiguration;  // 内在状态(共享)
    
    public ConcreteRobot(String model, String baseConfiguration) {
        this.model = model;
        this.baseConfiguration = baseConfiguration;
    }
    
    @Override
    public void operate(String location, String task) {
        System.out.println("🤖 机器人型号: " + model + 
                         " | 位置: " + location + 
                         " | 执行任务: " + task +
                         " | 基础配置: " + baseConfiguration);
    }
    
    @Override
    public String getModel() {
        return model;
    }
}

3. 享元工厂 (Flyweight Factory)

// 机器人工厂 - 享元工厂
public class RobotFactory {
    private static Map<String, Robot> robotPool = new HashMap<>();
    
    public static Robot getRobot(String model, String baseConfiguration) {
        String key = model + "-" + baseConfiguration;
        
        if (!robotPool.containsKey(key)) {
            robotPool.put(key, new ConcreteRobot(model, baseConfiguration));
            System.out.println("创建新机器人: " + key);
        } else {
            System.out.println("重用现有机器人: " + key);
        }
        
        return robotPool.get(key);
    }
    
    public static int getRobotCount() {
        return robotPool.size();
    }
}

4. 客户端代码 (Client Code)

// 客户端使用享元对象
public class RobotDeploymentSystem {
    public static void main(String[] args) {
        // 部署大量机器人,但只创建少量共享对象
        
        // 获取共享的机器人对象
        Robot robot1 = RobotFactory.getRobot("T-800", "Standard");
        Robot robot2 = RobotFactory.getRobot("T-800", "Standard"); // 重用
        Robot robot3 = RobotFactory.getRobot("T-1000", "Advanced");
        Robot robot4 = RobotFactory.getRobot("T-800", "Standard"); // 重用
        Robot robot5 = RobotFactory.getRobot("T-1000", "Advanced"); // 重用
        
        System.out.println("\n已创建机器人类型数量: " + RobotFactory.getRobotCount());
        
        // 使用不同的外在状态
        System.out.println("\n机器人部署情况:");
        robot1.operate("工厂A", "装配零件");
        robot2.operate("工厂B", "质量检测");
        robot3.operate("实验室", "科学研究");
        robot4.operate("仓库", "货物搬运");
        robot5.operate("控制室", "监控系统");
        
        // 部署更多机器人
        System.out.println("\n部署更多机器人:");
        for (int i = 0; i < 5; i++) {
            Robot robot = RobotFactory.getRobot("T-800", "Standard");
            robot.operate("位置" + i, "任务" + i);
        }
        
        System.out.println("\n最终创建的机器人类型数量: " + RobotFactory.getRobotCount());
    }
}

在Java标准库中的应用

享元模式在Java标准库中有多种应用:

  1. 字符串常量池

    String s1 = "hello";  // 从常量池获取
    String s2 = "hello";  // 重用同一个对象
    String s3 = new String("hello");  // 创建新对象
    System.out.println(s1 == s2);  // true - 同一个对象
    System.out.println(s1 == s3);  // false - 不同对象
    
  2. 包装类的缓存

    Integer i1 = Integer.valueOf(127);  // 使用缓存
    Integer i2 = Integer.valueOf(127);  // 重用缓存对象
    Integer i3 = Integer.valueOf(128);  // 创建新对象(超出缓存范围)
    System.out.println(i1 == i2);  // true
    System.out.println(i1 == i3);  // false
    
  3. 线程池

    ExecutorService executor = Executors.newFixedThreadPool(5);
    // 重用线程对象而不是频繁创建销毁
    for (int i = 0; i < 10; i++) {
        executor.execute(() -> {
            System.out.println("任务执行中: " + Thread.currentThread().getName());
        });
    }
    executor.shutdown();
    
  4. 连接池

    // 数据库连接池重用连接对象
    DataSource dataSource = // 获取数据源
    try (Connection conn = dataSource.getConnection()) {
        // 使用数据库连接
    }  // 连接返回到池中重用
    

总结

享元模式通过共享相似对象来优化大量细粒度对象的内存使用,特别适用于需要创建大量相似对象的场景。它将对象状态分为内在状态(共享)和外在状态(不共享),通过工厂模式管理共享对象。在机器人示例中,相同型号和配置的机器人共享同一个对象实例,只有位置和任务等外在状态不同,从而显著减少了内存占用。

posted @ 2025-09-06 15:55  庞去广  阅读(13)  评论(0)    收藏  举报