DukeDuke
主页
关于我们
主页
关于我们
  • Java

    • Java基础

      • 内存与磁盘
      • 进制转换
      • 数据存储
      • Java基本数据类型
      • HashMap
      • Java四大引用
    • JVM

      • 认识JVM
      • JVM类加载器
      • 运行时数据区
      • 执行引擎
      • 本地方法接口
      • 本地方法库
      • JVM垃圾回收
      • JVM性能监控
      • JVM调优
    • 设计模式
      • 单例模式
      • 工厂模式
      • 策略模式
      • 适配器模式
      • 建造者模式
      • 原型模式
      • 装饰器模式
      • 代理模式
      • 外观模式
      • 享元模式
      • 组合模式
      • 桥接模式
    • Java多线程

      • Java 线程基础详解
      • Java 线程池详解
      • Java ThreadLocal 详解
      • Java volatile 详解
      • Java 线程间通信详解
      • Java 线程安全详解
      • Java 线程调度详解
      • Java 线程优先级详解

      • Java 线程中断详解
      • Java 线程死锁详解
    • Java反射
    • Java 面试题

      • Java 基础概念面试题
      • Java 面向对象编程面试题
      • Java 集合框架面试题
      • Java 多线程与并发面试题
      • JVM 与内存管理面试题
      • Java I/O 与 NIO 面试题
      • Java 异常处理面试题
      • Java 反射与注解面试题
      • Java Spring 框架面试题
      • Java 数据库与 JDBC 面试题
      • Java 性能优化面试题
      • Java 实际项目经验面试题
      • Java 高级特性面试题
      • Java 面试准备建议

享元模式

概述

享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享技术有效地支持大量细粒度对象的复用。享元模式通过共享已经存在的对象来大幅度减少需要创建的对象数量,避免大量相似对象的开销,从而提高系统资源的利用率。

定义

享元模式:运用共享技术有效地支持大量细粒度对象的复用。

核心思想

  • 共享:将对象的公共部分提取出来,多个对象共享这些公共部分
  • 细粒度:对象被设计为细粒度的,可以共享
  • 不可变:享元对象的状态是不可变的,避免并发问题

结构

角色定义

  1. Flyweight(抽象享元类):定义对象的内部状态和外部状态的接口或实现
  2. ConcreteFlyweight(具体享元类):实现抽象享元类定义的接口
  3. UnsharedConcreteFlyweight(非共享具体享元类):不能被共享的子类
  4. FlyweightFactory(享元工厂类):创建并管理享元对象,确保合理地共享享元对象
  5. Client(客户端):维持一个对享元的引用,计算或存储享元的外部状态

类图

┌─────────────────┐    creates    ┌─────────────────────┐
│ FlyweightFactory│ ────────────► │   ConcreteFlyweight │
└─────────────────┘               └─────────────────────┘
         │                                  ▲
         │ uses                             │
         ▼                                  │
┌─────────────────┐               ┌─────────────────────┐
│     Client      │ ─────────────► │   AbstractFlyweight │
└─────────────────┘               └─────────────────────┘

实现示例

示例 1:文字编辑器中的字符对象

// 抽象享元类
public abstract class Character {
    protected char symbol;
    protected int size;
    protected String font;

    public abstract void display(int x, int y);
}

// 具体享元类
public class ConcreteCharacter extends Character {
    public ConcreteCharacter(char symbol, int size, String font) {
        this.symbol = symbol;
        this.size = size;
        this.font = font;
    }

    @Override
    public void display(int x, int y) {
        System.out.println("显示字符 '" + symbol + "' 在位置 (" + x + "," + y +
                          ") 使用字体 " + font + " 大小 " + size);
    }
}

// 享元工厂类
public class CharacterFactory {
    private Map<String, Character> characterPool = new HashMap<>();

    public Character getCharacter(char symbol, int size, String font) {
        String key = symbol + "_" + size + "_" + font;

        if (!characterPool.containsKey(key)) {
            characterPool.put(key, new ConcreteCharacter(symbol, size, font));
            System.out.println("创建新字符对象: " + key);
        } else {
            System.out.println("复用已存在的字符对象: " + key);
        }

        return characterPool.get(key);
    }

    public int getPoolSize() {
        return characterPool.size();
    }
}

// 客户端
public class TextEditor {
    private CharacterFactory factory = new CharacterFactory();
    private List<Character> characters = new ArrayList<>();

    public void addCharacter(char symbol, int size, String font, int x, int y) {
        Character character = factory.getCharacter(symbol, size, font);
        characters.add(character);
        character.display(x, y);
    }

    public void showPoolInfo() {
        System.out.println("字符池大小: " + factory.getPoolSize());
    }
}

示例 2:围棋棋子

// 抽象享元类
public abstract class ChessPiece {
    protected String color;

    public abstract void place(int x, int y);
}

// 具体享元类
public class BlackChessPiece extends ChessPiece {
    public BlackChessPiece() {
        this.color = "黑色";
    }

    @Override
    public void place(int x, int y) {
        System.out.println(color + "棋子放置在位置 (" + x + "," + y + ")");
    }
}

public class WhiteChessPiece extends ChessPiece {
    public WhiteChessPiece() {
        this.color = "白色";
    }

    @Override
    public void place(int x, int y) {
        System.out.println(color + "棋子放置在位置 (" + x + "," + y + ")");
    }
}

// 享元工厂类
public class ChessPieceFactory {
    private Map<String, ChessPiece> piecePool = new HashMap<>();

    public ChessPiece getChessPiece(String color) {
        if (!piecePool.containsKey(color)) {
            if ("黑色".equals(color)) {
                piecePool.put(color, new BlackChessPiece());
            } else if ("白色".equals(color)) {
                piecePool.put(color, new WhiteChessPiece());
            }
            System.out.println("创建新的" + color + "棋子");
        } else {
            System.out.println("复用已存在的" + color + "棋子");
        }

        return piecePool.get(color);
    }
}

// 客户端
public class GoGame {
    private ChessPieceFactory factory = new ChessPieceFactory();

    public void placePiece(String color, int x, int y) {
        ChessPiece piece = factory.getChessPiece(color);
        piece.place(x, y);
    }
}

使用场景

  1. 大量相似对象:系统中存在大量相似对象,需要缓冲池的场景
  2. 内存敏感:需要节省内存,提高性能的场景
  3. 对象状态分离:对象的大部分状态可以外部化,且可以将这些外部状态传入对象中
  4. 缓存:需要缓存大量对象的场景

典型应用

  • 文字编辑器中的字符对象
  • 图形编辑器中的图形对象
  • 游戏中的棋子、子弹等对象
  • 数据库连接池
  • 线程池

优缺点

优点

  1. 内存效率:大幅减少内存使用,特别是当有大量相似对象时
  2. 性能提升:减少对象创建和销毁的开销
  3. 资源共享:通过共享机制提高资源利用率
  4. 扩展性好:易于扩展新的享元类型

缺点

  1. 复杂性增加:增加了系统的复杂性
  2. 状态管理:需要仔细管理内部状态和外部状态
  3. 线程安全:享元对象通常是不可变的,需要考虑线程安全问题
  4. 调试困难:共享对象可能导致调试困难

注意事项

  1. 状态分离:正确区分内部状态和外部状态
  2. 不可变性:享元对象应该是不可变的
  3. 工厂管理:使用工厂类管理享元对象的创建和获取
  4. 内存监控:注意监控享元池的大小,避免内存泄漏

与其他模式的关系

  • 单例模式:享元工厂通常使用单例模式实现
  • 组合模式:享元模式可以与组合模式结合使用
  • 策略模式:享元对象可以包含策略对象

总结

享元模式是一种重要的结构型设计模式,特别适用于需要处理大量相似对象的场景。通过共享机制,它可以显著提高系统的性能和内存效率。在实际应用中,需要根据具体场景合理使用,并注意状态管理和线程安全等问题。

最近更新:: 2025/12/29 11:07
Contributors: Duke
Prev
外观模式
Next
组合模式