享元模式全方位深度解析

0 阅读17分钟

一、模式定义与核心思想

享元模式通过共享技术有效地支持大量细粒度对象的复用。它使用共享对象来最小化内存使用或计算开销,特别适用于存在大量相似对象的场景。享元模式的核心是将对象的状态分为内部状态(Intrinsic State)和外部状态(Extrinsic State)。

  • 内部状态:对象共享的、不可变的部分,存储在享元对象内部
  • 外部状态:对象特有的、可变的部分,由客户端在调用时传入

二、Java源代码实现

1. 基础享元模式实现

java
java
下载
复制
import java.util.HashMap;
import java.util.Map;

/**
 * 享元接口
 */
interface Shape {
    void draw(int x, int y, String color);  // 外部状态作为参数传入
}

/**
 * 具体享元类 - 圆形
 */
class Circle implements Shape {
    private String type;          // 内部状态:类型(共享)
    private String color;         // 内部状态:颜色(共享)
    private int radius;           // 内部状态:半径(共享)
    
    public Circle(String color) {
        this.type = "Circle";
        this.color = color;
        this.radius = 10; // 默认半径
    }
    
    @Override
    public void draw(int x, int y, String borderColor) {
        System.out.println(String.format(
            "绘制%s: 颜色=%s, 边框色=%s, 半径=%d, 位置=(%d, %d)",
            type, color, borderColor, radius, x, y
        ));
    }
    
    // 内部状态getter
    public String getColor() {
        return color;
    }
}

/**
 * 享元工厂 - 管理共享的享元对象
 */
class ShapeFactory {
    private static final Map<String, Shape> circleMap = new HashMap<>();
    
    public static Shape getCircle(String color) {
        // 使用颜色作为键来查找圆对象
        Circle circle = (Circle) circleMap.get(color);
        
        if (circle == null) {
            // 如果不存在,则创建新对象并放入池中
            circle = new Circle(color);
            circleMap.put(color, circle);
            System.out.println("创建新的圆形,颜色: " + color);
        } else {
            System.out.println("复用已有圆形,颜色: " + color);
        }
        
        return circle;
    }
    
    public static int getCircleCount() {
        return circleMap.size();
    }
}

2. 复杂享元模式实现(带外部状态)

java
java
下载
复制
import java.util.ArrayList;
import java.util.List;

/**
 * 享元对象 - 字符对象
 */
class CharacterFlyweight {
    private char symbol;          // 内部状态:字符(共享)
    private String fontFamily;    // 内部状态:字体(共享)
    private int fontSize;         // 内部状态:字号(共享)
    
    public CharacterFlyweight(char symbol, String fontFamily, int fontSize) {
        this.symbol = symbol;
        this.fontFamily = fontFamily;
        this.fontSize = fontSize;
    }
    
    public void render(int positionX, int positionY, String color) {
        // 外部状态:位置和颜色
        System.out.println(String.format(
            "渲染字符 '%c' [字体: %s, 字号: %d] 在位置(%d, %d),颜色: %s",
            symbol, fontFamily, fontSize, positionX, positionY, color
        ));
    }
    
    // 内部状态唯一标识
    @Override
    public String toString() {
        return symbol + "-" + fontFamily + "-" + fontSize;
    }
}

/**
 * 享元工厂 - 带缓存管理
 */
class CharacterFlyweightFactory {
    private static final Map<String, CharacterFlyweight> cache = new HashMap<>();
    
    public static CharacterFlyweight getCharacter(char symbol, String fontFamily, int fontSize) {
        String key = symbol + "-" + fontFamily + "-" + fontSize;
        
        if (!cache.containsKey(key)) {
            cache.put(key, new CharacterFlyweight(symbol, fontFamily, fontSize));
            System.out.println("创建新字符对象: " + key);
        }
        
        return cache.get(key);
    }
    
    public static int getCacheSize() {
        return cache.size();
    }
    
    public static void clearCache() {
        cache.clear();
        System.out.println("缓存已清空");
    }
}

/**
 * 外部状态容器 - 维护外部状态与享元对象的关联
 */
class TextCharacter {
    private CharacterFlyweight flyweight;  // 享元对象(内部状态)
    private int positionX;                 // 外部状态
    private int positionY;                 // 外部状态
    private String color;                  // 外部状态
    
    public TextCharacter(CharacterFlyweight flyweight, int positionX, int positionY, String color) {
        this.flyweight = flyweight;
        this.positionX = positionX;
        this.positionY = positionY;
        this.color = color;
    }
    
    public void render() {
        flyweight.render(positionX, positionY, color);
    }
}

/**
 * 文档对象 - 使用享元模式
 */
class Document {
    private List<TextCharacter> characters = new ArrayList<>();
    
    public void addCharacter(char symbol, String fontFamily, int fontSize, 
                            int x, int y, String color) {
        // 获取或创建享元对象
        CharacterFlyweight flyweight = CharacterFlyweightFactory.getCharacter(
            symbol, fontFamily, fontSize
        );
        
        // 创建包含外部状态的对象
        TextCharacter textChar = new TextCharacter(flyweight, x, y, color);
        characters.add(textChar);
    }
    
    public void render() {
        System.out.println("\n===== 渲染文档 =====");
        for (TextCharacter ch : characters) {
            ch.render();
        }
        System.out.println("总字符数: " + characters.size());
        System.out.println("享元对象数: " + CharacterFlyweightFactory.getCacheSize());
        System.out.println("内存节省: " + 
            (characters.size() - CharacterFlyweightFactory.getCacheSize()) + " 个对象");
    }
}

3. 客户端调用示例

java
java
下载
复制
public class Client {
    public static void main(String[] args) {
        System.out.println("===== 示例1: 基础享元模式 =====");
        demoBasicFlyweight();
        
        System.out.println("\n===== 示例2: 复杂享元模式 =====");
        demoComplexFlyweight();
        
        System.out.println("\n===== 示例3: 性能对比 =====");
        demoPerformanceComparison();
    }
    
    private static void demoBasicFlyweight() {
        // 获取圆形对象
        Shape redCircle1 = ShapeFactory.getCircle("红色");
        redCircle1.draw(10, 10, "黑色");
        
        Shape redCircle2 = ShapeFactory.getCircle("红色");  // 复用
        redCircle2.draw(20, 20, "白色");
        
        Shape blueCircle = ShapeFactory.getCircle("蓝色");   // 新建
        blueCircle.draw(30, 30, "黑色");
        
        Shape greenCircle1 = ShapeFactory.getCircle("绿色"); // 新建
        greenCircle1.draw(40, 40, "黄色");
        
        Shape greenCircle2 = ShapeFactory.getCircle("绿色"); // 复用
        greenCircle2.draw(50, 50, "红色");
        
        System.out.println("圆形对象总数: 5");
        System.out.println("实际创建的圆形对象数: " + ShapeFactory.getCircleCount());
    }
    
    private static void demoComplexFlyweight() {
        Document doc = new Document();
        
        // 添加字符到文档
        doc.addCharacter('H', "Arial", 12, 0, 0, "黑色");
        doc.addCharacter('e', "Arial", 12, 10, 0, "黑色");
        doc.addCharacter('l', "Arial", 12, 20, 0, "黑色");
        doc.addCharacter('l', "Arial", 12, 30, 0, "红色");  // 相同字符,不同颜色
        doc.addCharacter('o', "Arial", 12, 40, 0, "黑色");
        
        doc.addCharacter('W', "Times New Roman", 14, 0, 20, "蓝色");
        doc.addCharacter('o', "Times New Roman", 14, 15, 20, "蓝色");
        doc.addCharacter('r', "Times New Roman", 14, 30, 20, "蓝色");
        doc.addCharacter('l', "Arial", 12, 45, 20, "绿色");  // 相同字符,不同字体
        doc.addCharacter('d', "Times New Roman", 14, 60, 20, "蓝色");
        
        // 渲染文档
        doc.render();
    }
    
    private static void demoPerformanceComparison() {
        System.out.println("非享元方式创建10000个字符对象:");
        long start1 = System.currentTimeMillis();
        createCharactersWithoutFlyweight(10000);
        long end1 = System.currentTimeMillis();
        System.out.println("耗时: " + (end1 - start1) + "ms");
        System.out.println("内存使用: 10000个对象");
        
        System.out.println("\n享元方式创建10000个字符对象:");
        long start2 = System.currentTimeMillis();
        createCharactersWithFlyweight(10000);
        long end2 = System.currentTimeMillis();
        System.out.println("耗时: " + (end2 - start2) + "ms");
        System.out.println("内存使用: " + CharacterFlyweightFactory.getCacheSize() + "个对象");
        
        // 清理缓存
        CharacterFlyweightFactory.clearCache();
    }
    
    private static void createCharactersWithoutFlyweight(int count) {
        // 非享元方式:每个字符都创建新对象
        List<Character> characters = new ArrayList<>();
        char[] symbols = {'A', 'B', 'C', 'D', 'E'};
        
        for (int i = 0; i < count; i++) {
            char symbol = symbols[i % symbols.length];
            // 每次都创建新对象
            CharacterFlyweight ch = new CharacterFlyweight(symbol, "Arial", 12);
            characters.add((Character) null); // 模拟使用
        }
    }
    
    private static void createCharactersWithFlyweight(int count) {
        // 享元方式:复用对象
        char[] symbols = {'A', 'B', 'C', 'D', 'E'};
        
        for (int i = 0; i < count; i++) {
            char symbol = symbols[i % symbols.length];
            // 从工厂获取,会复用已存在的对象
            CharacterFlyweight ch = CharacterFlyweightFactory.getCharacter(symbol, "Arial", 12);
        }
    }
}

三、应用场景深度解析

1. 文本编辑器/文档处理系统

java
java
下载
复制
/**
 * 实际应用:富文本编辑器
 */
class RichTextEditor {
    private static class FontStyle {
        String family;
        int size;
        boolean bold;
        boolean italic;
        
        FontStyle(String family, int size, boolean bold, boolean italic) {
            this.family = family;
            this.size = size;
            this.bold = bold;
            this.italic = italic;
        }
        
        @Override
        public int hashCode() {
            return (family + size + bold + italic).hashCode();
        }
        
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof FontStyle)) return false;
            FontStyle other = (FontStyle) obj;
            return family.equals(other.family) && size == other.size &&
                   bold == other.bold && italic == other.italic;
        }
    }
    
    private static class FontStyleFactory {
        private static final Map<Integer, FontStyle> cache = new HashMap<>();
        
        public static FontStyle getFontStyle(String family, int size, 
                                            boolean bold, boolean italic) {
            FontStyle style = new FontStyle(family, size, bold, italic);
            int key = style.hashCode();
            
            if (!cache.containsKey(key)) {
                cache.put(key, style);
            }
            
            return cache.get(key);
        }
    }
}

2. 游戏开发 - 粒子系统/单位管理

java
java
下载
复制
/**
 * 游戏单位享元模式
 */
class GameUnitFlyweight {
    // 内部状态(共享)
    private String unitType;      // 单位类型
    private String texture;       // 纹理
    private int baseHealth;       // 基础生命值
    private int baseDamage;       // 基础攻击力
    private int moveSpeed;        // 移动速度
    
    public GameUnitFlyweight(String unitType, String texture) {
        this.unitType = unitType;
        this.texture = texture;
        // 根据类型设置基础属性
        switch(unitType) {
            case "Archer":
                this.baseHealth = 50;
                this.baseDamage = 15;
                this.moveSpeed = 3;
                break;
            case "Warrior":
                this.baseHealth = 100;
                this.baseDamage = 25;
                this.moveSpeed = 2;
                break;
            case "Mage":
                this.baseHealth = 40;
                this.baseDamage = 30;
                this.moveSpeed = 2;
                break;
        }
    }
    
    public void render(int x, int y, int health, int level) {
        // 外部状态:位置、当前生命值、等级
        System.out.println(String.format(
            "渲染%s [纹理: %s] 在(%d, %d), 生命: %d/%d, 等级: %d",
            unitType, texture, x, y, health, baseHealth, level
        ));
    }
}

/**
 * 游戏单位实例(包含外部状态)
 */
class GameUnit {
    private GameUnitFlyweight flyweight;  // 享元对象
    private int x, y;                     // 外部状态:位置
    private int currentHealth;            // 外部状态:当前生命值
    private int level;                    // 外部状态:等级
    private String owner;                 // 外部状态:所有者
    
    public GameUnit(GameUnitFlyweight flyweight, int x, int y, String owner) {
        this.flyweight = flyweight;
        this.x = x;
        this.y = y;
        this.currentHealth = flyweight.baseHealth;
        this.level = 1;
        this.owner = owner;
    }
    
    public void render() {
        flyweight.render(x, y, currentHealth, level);
    }
}

3. GUI组件库

java
java
下载
复制
/**
 * GUI按钮享元模式
 */
class ButtonFlyweight {
    // 内部状态
    private String style;         // 样式主题
    private String font;          // 字体
    private String backgroundColor; // 背景色
    private String borderStyle;   // 边框样式
    
    public ButtonFlyweight(String style) {
        this.style = style;
        // 根据样式初始化
        if ("Modern".equals(style)) {
            this.font = "Arial";
            this.backgroundColor = "#3498db";
            this.borderStyle = "rounded";
        } else if ("Classic".equals(style)) {
            this.font = "Times New Roman";
            this.backgroundColor = "#2ecc71";
            this.borderStyle = "square";
        }
    }
    
    public void render(String text, int x, int y, int width, int height) {
        // 外部状态:文本、位置、尺寸
        System.out.println(String.format(
            "渲染%s按钮: '%s' 在(%d, %d), 尺寸: %dx%d\n" +
            "字体: %s, 背景: %s, 边框: %s",
            style, text, x, y, width, height,
            font, backgroundColor, borderStyle
        ));
    }
}

4. 数据库连接池

java
java
下载
复制
/**
 * 连接池享元模式实现
 */
class ConnectionFlyweight {
    // 内部状态
    private String url;
    private String username;
    private String password;
    private String driverClass;
    
    // 实际连接(模拟)
    private boolean inUse;
    
    public ConnectionFlyweight(String url, String username, 
                              String password, String driverClass) {
        this.url = url;
        this.username = username;
        this.password = password;
        this.driverClass = driverClass;
        this.inUse = false;
        System.out.println("创建数据库连接: " + url);
    }
    
    public void executeQuery(String sql, Map<String, Object> params) {
        // 外部状态:SQL和参数
        System.out.println("执行查询: " + sql);
        System.out.println("参数: " + params);
        System.out.println("使用连接: " + url);
    }
    
    public boolean isInUse() {
        return inUse;
    }
    
    public void setInUse(boolean inUse) {
        this.inUse = inUse;
    }
}

class ConnectionPool {
    private static final Map<String, List<ConnectionFlyweight>> pools = new HashMap<>();
    private static final int MAX_POOL_SIZE = 10;
    
    public static synchronized ConnectionFlyweight getConnection(
            String url, String username, String password, String driverClass) {
        
        String key = url + username + driverClass;
        List<ConnectionFlyweight> pool = pools.getOrDefault(key, new ArrayList<>());
        
        // 查找空闲连接
        for (ConnectionFlyweight conn : pool) {
            if (!conn.isInUse()) {
                conn.setInUse(true);
                System.out.println("复用现有连接");
                return conn;
            }
        }
        
        // 创建新连接(如果未达到上限)
        if (pool.size() < MAX_POOL_SIZE) {
            ConnectionFlyweight newConn = new ConnectionFlyweight(
                url, username, password, driverClass
            );
            newConn.setInUse(true);
            pool.add(newConn);
            pools.put(key, pool);
            return newConn;
        }
        
        throw new RuntimeException("连接池已满");
    }
    
    public static void releaseConnection(ConnectionFlyweight conn) {
        conn.setInUse(false);
    }
}

四、优缺点深度解析

优点

  1. 极大减少内存使用

    • 共享内部状态,避免重复创建相同对象
    • 对于大量相似对象,内存节省效果显著
    • 示例:10000个相同字符对象 → 1个享元对象 + 10000个外部状态
  2. 提高性能

    • 减少对象创建和销毁的开销
    • 降低垃圾回收压力
    • 特别适合创建成本高的对象
  3. 统一管理共享对象

    • 集中控制共享对象的生命周期
    • 便于实现对象池、缓存等机制
    • 支持对象的延迟初始化
  4. 减少系统复杂度

    • 将对象状态分离为内部和外部
    • 客户端代码更简洁
    • 便于维护和扩展
  5. 支持大规模对象集合

    • 能够处理传统方式无法处理的大规模对象
    • 在内存有限的情况下仍能工作

缺点

  1. 增加系统复杂度

    • 需要区分内部状态和外部状态
    • 增加了设计和实现的难度
    • 可能引入线程安全问题
  2. 外部状态管理复杂

    • 客户端需要维护外部状态
    • 外部状态的传递增加了方法参数
    • 可能影响代码可读性
  3. 线程安全问题

    • 共享对象可能被多个线程同时访问
    • 需要额外的同步机制
    • 可能降低并发性能
  4. 不适用于所有场景

    • 对象差异较大时效果有限
    • 外部状态过多可能抵消收益
    • 不适合需要独立对象实例的场景
  5. 调试困难

    • 对象状态分散在内部和外部
    • 调试时难以追踪完整状态
    • 可能引入隐式错误

五、使用要点与最佳实践

1. 正确识别内部状态和外部状态

java
java
下载
复制
/**
 * 正确识别状态的示例
 */
class GoodFlyweightDesign {
    // 内部状态(共享,不变)
    static class IntrinsicState {
        String type;        // 类型
        String texture;     // 纹理
        String model;       // 模型
        int baseAttributes; // 基础属性
        
        @Override
        public boolean equals(Object o) {
            // 基于内部状态实现equals和hashCode
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            IntrinsicState that = (IntrinsicState) o;
            return Objects.equals(type, that.type) &&
                   Objects.equals(texture, that.texture) &&
                   Objects.equals(model, that.model);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(type, texture, model);
        }
    }
    
    // 外部状态(不共享,变化)
    static class ExtrinsicState {
        int positionX;
        int positionY;
        int currentHealth;
        String owner;
        // ... 其他变化的状态
    }
}

2. 工厂模式与享元模式的结合

java
java
下载
复制
/**
 * 享元工厂的最佳实践
 */
class OptimizedFlyweightFactory {
    // 使用WeakReference避免内存泄漏
    private static final Map<String, WeakReference<Flyweight>> cache = 
        new ConcurrentHashMap<>();
    
    // 使用双重检查锁定确保线程安全
    public static Flyweight getFlyweight(String key) {
        WeakReference<Flyweight> ref = cache.get(key);
        Flyweight flyweight = (ref != null) ? ref.get() : null;
        
        if (flyweight == null) {
            synchronized (OptimizedFlyweightFactory.class) {
                ref = cache.get(key);
                flyweight = (ref != null) ? ref.get() : null;
                
                if (flyweight == null) {
                    flyweight = createFlyweight(key);
                    cache.put(key, new WeakReference<>(flyweight));
                }
            }
        }
        
        return flyweight;
    }
    
    // 定期清理失效的引用
    public static void cleanup() {
        cache.entrySet().removeIf(entry -> 
            entry.getValue().get() == null
        );
    }
}

3. 线程安全实现

java
java
下载
复制
/**
 * 线程安全的享元模式
 */
class ThreadSafeFlyweight {
    // 使用ConcurrentHashMap保证线程安全
    private static final ConcurrentHashMap<String, Flyweight> cache = 
        new ConcurrentHashMap<>();
    
    // 使用computeIfAbsent原子操作
    public static Flyweight getFlyweight(String key) {
        return cache.computeIfAbsent(key, k -> {
            System.out.println("创建新享元对象: " + k);
            return createFlyweight(k);
        });
    }
    
    // 使用不可变对象作为享元
    static class ImmutableFlyweight {
        private final String data;  // final保证不可变
        
        public ImmutableFlyweight(String data) {
            this.data = data;
        }
        
        // 只读方法,线程安全
        public void operation(String extrinsicState) {
            System.out.println("处理: " + data + " with " + extrinsicState);
        }
    }
}

4. 性能优化技巧

java
java
下载
复制
class PerformanceOptimizedFlyweight {
    // 1. 使用枚举实现单例享元
    enum SingletonFlyweight implements Flyweight {
        INSTANCE_A,
        INSTANCE_B,
        INSTANCE_C;
        
        @Override
        public void operation(String state) {
            // 实现
        }
    }
    
    // 2. 使用享元池控制对象数量
    static class FlyweightPool {
        private final BlockingQueue<Flyweight> pool;
        private final int maxSize;
        
        public FlyweightPool(int maxSize, Supplier<Flyweight> creator) {
            this.maxSize = maxSize;
            this.pool = new ArrayBlockingQueue<>(maxSize);
            
            // 预创建对象
            for (int i = 0; i < maxSize / 2; i++) {
                pool.offer(creator.get());
            }
        }
        
        public Flyweight acquire() throws InterruptedException {
            Flyweight flyweight = pool.poll();
            if (flyweight == null) {
                flyweight = createFlyweight();
            }
            return flyweight;
        }
        
        public void release(Flyweight flyweight) {
            if (pool.size() < maxSize) {
                pool.offer(flyweight);
            }
        }
    }
    
    // 3. 使用缓存策略(LRU)
    static class LRUFlyweightCache {
        private final LinkedHashMap<String, Flyweight> cache;
        private final int capacity;
        
        public LRUFlyweightCache(int capacity) {
            this.capacity = capacity;
            this.cache = new LinkedHashMap<String, Flyweight>(
                capacity, 0.75f, true) {
                @Override
                protected boolean removeEldestEntry(Map.Entry eldest) {
                    return size() > capacity;
                }
            };
        }
        
        public synchronized Flyweight get(String key) {
            return cache.get(key);
        }
        
        public synchronized void put(String key, Flyweight value) {
            cache.put(key, value);
        }
    }
}

六、与其他模式的关系与对比

1. 享元模式 vs 单例模式

特性享元模式单例模式
目的对象共享,减少内存使用全局唯一实例
对象数量多个(按类别)一个
状态有内部状态和外部状态通常无状态或有全局状态
使用场景大量相似对象全局访问点

2. 享元模式 vs 原型模式

特性享元模式原型模式
目的共享对象,减少创建快速创建相似对象
对象关系共享同一对象克隆新对象
状态处理分离内部/外部状态完全复制状态
性能内存优化创建性能优化

3. 享元模式与组合模式结合

java
java
下载
复制
/**
 * 享元模式 + 组合模式:文档结构树
 */
class CompositeFlyweightExample {
    // 组件接口
    interface DocumentComponent {
        void render(String style);
    }
    
    // 叶子节点 - 字符(享元)
    static class Character implements DocumentComponent {
        private char symbol;
        private FontFlyweight font;  // 享元对象
        
        public Character(char symbol, FontFlyweight font) {
            this.symbol = symbol;
            this.font = font;
        }
        
        @Override
        public void render(String style) {
            font.render(symbol, style);
        }
    }
    
    // 复合节点 - 段落
    static class Paragraph implements DocumentComponent {
        private List<DocumentComponent> children = new ArrayList<>();
        
        public void add(DocumentComponent component) {
            children.add(component);
        }
        
        @Override
        public void render(String style) {
            for (DocumentComponent child : children) {
                child.render(style);
            }
        }
    }
}

七、实际框架中的应用

1. Java String池

java
java
下载
复制
/**
 * Java String的享元模式实现
 */
class StringFlyweightExample {
    public static void demoStringPool() {
        // String内部使用享元模式
        String s1 = "Hello";           // 放入字符串常量池
        String s2 = "Hello";           // 复用常量池中的"Hello"
        String s3 = new String("Hello"); // 创建新对象
        
        System.out.println("s1 == s2: " + (s1 == s2));       // true,相同对象
        System.out.println("s1 == s3: " + (s1 == s3));       // false,不同对象
        System.out.println("s1.equals(s3): " + s1.equals(s3)); // true,内容相同
        
        // intern()方法返回字符串在常量池中的引用
        String s4 = s3.intern();
        System.out.println("s1 == s4: " + (s1 == s4));       // true
    }
}

2. Integer缓存

java
java
下载
复制
/**
 * Java Integer缓存(享元模式)
 */
class IntegerCacheExample {
    public static void demoIntegerCache() {
        // Integer缓存-128到127
        Integer i1 = 127;
        Integer i2 = 127;
        Integer i3 = 128;
        Integer i4 = 128;
        
        System.out.println("i1 == i2 (127): " + (i1 == i2)); // true,缓存命中
        System.out.println("i3 == i4 (128): " + (i3 == i4)); // false,超出缓存范围
        
        // 手动获取缓存
        Integer i5 = Integer.valueOf(100);  // 使用缓存
        Integer i6 = new Integer(100);      // 创建新对象
        System.out.println("i5 == i6: " + (i5 == i6)); // false
    }
}

3. 数据库连接池实现

java
java
下载
复制
/**
 * 实际数据库连接池实现(简化版)
 */
class RealConnectionPool {
    private final BlockingQueue<Connection> pool;
    private final int maxSize;
    private final String url;
    private final String username;
    private final String password;
    
    // 享元:连接配置(内部状态)
    private static class ConnectionConfig {
        final String url;
        final String driver;
        final Properties properties;
        
        ConnectionConfig(String url, String driver, Properties props) {
            this.url = url;
            this.driver = driver;
            this.properties = props;
        }
        
        @Override
        public boolean equals(Object o) {
            // 基于配置判断是否相等
            if (this == o) return true;
            if (!(o instanceof ConnectionConfig)) return false;
            ConnectionConfig that = (ConnectionConfig) o;
            return url.equals(that.url) && 
                   driver.equals(that.driver) && 
                   properties.equals(that.properties);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(url, driver, properties);
        }
    }
    
    // 连接工厂(享元工厂)
    private static class ConnectionFactory {
        private static final Map<ConnectionConfig, RealConnectionPool> pools = 
            new HashMap<>();
        
        public static synchronized RealConnectionPool getPool(
                String url, String username, String password, 
                String driver, int maxSize) {
            
            Properties props = new Properties();
            props.setProperty("user", username);
            props.setProperty("password", password);
            
            ConnectionConfig config = new ConnectionConfig(url, driver, props);
            
            if (!pools.containsKey(config)) {
                pools.put(config, new RealConnectionPool(url, username, password, maxSize));
            }
            
            return pools.get(config);
        }
    }
}

八、反模式与常见错误

1. 错误:混淆内部状态和外部状态

java
java
下载
复制
class BadFlyweightDesign {
    // 错误:将可变状态放在享元中
    class BadFlyweight {
        String sharedData;      // 内部状态
        String changingData;    // 错误:可变状态不应在享元中
        
        void operation() {
            // 这里会修改共享状态!
            changingData = "modified";
        }
    }
    
    // 正确做法
    class GoodFlyweight {
        final String sharedData;  // 内部状态,final确保不变
        
        GoodFlyweight(String data) {
            this.sharedData = data;
        }
        
        void operation(String extrinsicData) {  // 外部状态作为参数
            // 使用但不修改共享状态
            System.out.println(sharedData + " with " + extrinsicData);
        }
    }
}

2. 错误:过度使用享元

java
java
下载
复制
class OveruseFlyweight {
    // 错误:为每个小对象都使用享元
    class TinyObject {
        int value;  // 只有4字节
        
        TinyObject(int value) {
            this.value = value;
        }
    }
    
    // 问题:享元工厂本身开销可能超过节省的内存
    class TinyObjectFactory {
        Map<Integer, TinyObject> cache = new HashMap<>();
        
        TinyObject getTinyObject(int value) {
            return cache.computeIfAbsent(value, TinyObject::new);
        }
    }
    
    // 正确:只有对象创建成本高或内存占用大时才使用享元
}

3. 错误:忽略线程安全

java
java
下载
复制
class ThreadUnsafeFlyweight {
    static class UnsafeFactory {
        private static Map<String, Object> cache = new HashMap<>();  // 非线程安全
        
        public static Object get(String key) {
            // 竞态条件:多个线程可能同时创建对象
            if (!cache.containsKey(key)) {
                cache.put(key, new ExpensiveObject());
            }
            return cache.get(key);
        }
    }
    
    static class SafeFactory {
        private static final ConcurrentHashMap<String, Object> cache = 
            new ConcurrentHashMap<>();
        
        public static Object get(String key) {
            // 使用computeIfAbsent保证原子性
            return cache.computeIfAbsent(key, k -> new ExpensiveObject());
        }
    }
}

九、性能测试与评估

java
java
下载
复制
class FlyweightBenchmark {
    static class ExpensiveObject {
        private final byte[] data = new byte[1024 * 1024]; // 1MB
        private final String id;
        
        public ExpensiveObject(String id) {
            this.id = id;
            // 模拟昂贵的初始化
            try { Thread.sleep(10); } catch (InterruptedException e) {}
        }
    }
    
    public static void main(String[] args) {
        int objectCount = 1000;
        String[] ids = {"A", "B", "C", "D", "E"}; // 只有5种类型
        
        System.out.println("测试对象数: " + objectCount);
        System.out.println("对象类型数: " + ids.length);
        
        // 测试非享元模式
        System.out.println("\n=== 非享元模式 ===");
        long start1 = System.currentTimeMillis();
        long memory1 = Runtime.getRuntime().freeMemory();
        
        List<ExpensiveObject> list1 = new ArrayList<>();
        for (int i = 0; i < objectCount; i++) {
            String id = ids[i % ids.length];
            list1.add(new ExpensiveObject(id));
        }
        
        long time1 = System.currentTimeMillis() - start1;
        long memoryUsed1 = memory1 - Runtime.getRuntime().freeMemory();
        System.out.println("耗时: " + time1 + "ms");
        System.out.println("内存使用: " + memoryUsed1 / 1024 / 1024 + "MB");
        
        // 测试享元模式
        System.out.println("\n=== 享元模式 ===");
        long start2 = System.currentTimeMillis();
        long memory2 = Runtime.getRuntime().freeMemory();
        
        Map<String, ExpensiveObject> flyweightCache = new HashMap<>();
        List<ExpensiveObject> list2 = new ArrayList<>();
        
        for (int i = 0; i < objectCount; i++) {
            String id = ids[i % ids.length];
            ExpensiveObject obj = flyweightCache.get(id);
            if (obj == null) {
                obj = new ExpensiveObject(id);
                flyweightCache.put(id, obj);
            }
            list2.add(obj);
        }
        
        long time2 = System.currentTimeMillis() - start2;
        long memoryUsed2 = memory2 - Runtime.getRuntime().freeMemory();
        System.out.println("耗时: " + time2 + "ms");
        System.out.println("内存使用: " + memoryUsed2 / 1024 / 1024 + "MB");
        
        System.out.println("\n=== 性能对比 ===");
        System.out.println("时间节省: " + (time1 - time2) + "ms (" + 
            (int)((1 - (double)time2/time1) * 100) + "%)");
        System.out.println("内存节省: " + (memoryUsed1 - memoryUsed2) / 1024 / 1024 + 
            "MB (" + (int)((1 - (double)memoryUsed2/memoryUsed1) * 100) + "%)");
    }
}

十、总结与最佳实践指南

1. 何时使用享元模式

  • 对象数量巨大:系统需要创建大量相似对象
  • 内存是瓶颈:对象占用内存大,需要优化内存使用
  • 对象状态可分离:能够清晰区分内部状态和外部状态
  • 对象大部分状态可共享:只有少部分状态需要变化
  • 对象创建成本高:创建对象需要大量资源或时间

2. 何时避免享元模式

  • 对象差异大:每个对象都有独特的状态
  • 外部状态过多:外部状态复杂,管理成本高
  • 线程安全要求高:难以保证共享对象的线程安全
  • 性能不是关键问题:系统内存充足,不需要优化
  • 对象生命周期短:频繁创建销毁,享元收益有限

3. 实施步骤

  1. 识别可共享状态:分析对象,找出不变的部分作为内部状态
  2. 设计享元接口:定义操作外部状态的方法
  3. 实现具体享元:存储内部状态,实现操作方法
  4. 创建享元工厂:管理享元对象的创建和共享
  5. 客户端使用:通过工厂获取享元,传递外部状态

4. 性能调优建议

  • 监控缓存命中率:确保享元模式真正带来性能提升
  • 合理设置缓存大小:避免内存泄漏或缓存过大
  • 使用弱引用:防止缓存导致的内存泄漏
  • 考虑并发性能:在高并发场景下优化同步机制
  • 定期清理缓存:移除不再使用的享元对象

5. 与其他模式结合

  • 工厂模式:管理享元对象的创建
  • 组合模式:构建树形结构的享元对象
  • 状态模式:享元对象内部状态变化
  • 策略模式:动态改变享元对象的行为

享元模式是优化系统性能的重要工具,特别是在处理大量相似对象的场景中。正确应用享元模式可以显著减少内存使用、提高性能,但需要仔细设计内部状态和外部状态的分离,并处理好线程安全和对象生命周期管理。在实际项目中,享元模式经常与缓存、对象池等技术结合使用,是高性能系统设计的关键模式之一。