设计模式中,Flyweight 模式的理解和示例、以及Flyweight 模式 vs Singleton 模式 区别对比,一文详解。

Flyweight 模式 vs Singleton 模式

Flyweight 模式示例(完整)

import java.util.HashMap;
import java.util.Map;

// 享元类:Student
public class Student {
    // 缓存池
    private static final Map<String, Student> cache = new HashMap<>();

    // 静态工厂方法
    public static Student create(int id, String name) {
        String key = id + "\n" + name;
        // 查询缓存
        Student std = cache.get(key);
        if (std == null) {
            // 未找到,创建新对象
            System.out.println(String.format("create new Student(%s, %s)", id, name));
            std = new Student(id, name);
            cache.put(key, std);
        } else {
            // 缓存中存在
            System.out.println(String.format("return cached Student(%s, %s)", std.id, std.name));
        }
        return std;
    }

    private final int id;
    private final String name;

    private Student(int id, String name) { // 私有构造器,保证通过工厂方法创建
        this.id = id;
        this.name = name;
    }

    public void show() {
        System.out.println("Student{id=" + id + ", name=" + name + "}");
    }

    public static void main(String[] args) {
        Student s1 = Student.create(1, "Alice");
        Student s2 = Student.create(1, "Alice");
        Student s3 = Student.create(2, "Bob");

        s1.show();
        s2.show();
        s3.show();

        // 验证共享对象
        System.out.println("s1 == s2? " + (s1 == s2)); // true
        System.out.println("s1 == s3? " + (s1 == s3)); // false
    }
}

运行示例输出

create new Student(1, Alice)
return cached Student(1, Alice)
create new Student(2, Bob)
Student{id=1, name=Alice}
Student{id=1, name=Alice}
Student{id=2, name=Bob}
s1 == s2? true
s1 == s3? false

解释

  • 缓存复用:相同 id+name 的 Student 对象只会创建一次,后续请求直接返回缓存实例。
  • 不可变对象:Student 的 id 和 name 是 final 的,确保共享安全。
  • 工厂方法:通过 Student.create() 创建对象,禁止直接 new Student(),确保使用缓存。

Flyweight vs Singleton 对比

特性 Singleton(单例模式) Flyweight(享元模式)
目的 保证类只有一个实例,全局共享 支持大量细粒度对象共享,减少重复实例
对象数量 只有一个对象 多个对象,但相同状态共享实例
使用场景 配置管理、日志系统、线程池管理、全局服务实例 缓存、图形对象、不可变对象复用(如Integer.valueOf)
关键点 构造器私有、静态实例、全局访问 不可变对象、工厂方法、内部缓存池
状态特点 全局唯一,无状态或可管理全局状态 可分为内部状态(共享)和外部状态(可变)
内存占用 只占用一份内存 多个对象共享内存,节省重复创建

一句话总结:

  • Singleton → “只有一个,全局共享”。
  • Flyweight → “共享重复对象,多份对象复用相同实例”。

对比示意图

Singleton:

+-----------------+
|   Singleton     |  <-- 全局唯一实例
+-----------------+

Flyweight:

+-----------------+      +-----------------+
|  Flyweight obj  |----> |  Flyweight obj  |  <-- 多个共享实例
+-----------------+      +-----------------+
          ^
          |
    FlyweightFactory
      维护缓存

具体示例对比

1️⃣ Singleton 示例:全局日志管理器

// 单例类:Logger
public class Logger {
    private static Logger instance;

    private Logger() {} // 私有构造器

    public static Logger getInstance() {
        if (instance == null) {
            instance = new Logger();
        }
        return instance;
    }

    public void log(String msg) {
        System.out.println("LOG: " + msg);
    }

    public static void main(String[] args) {
        Logger l1 = Logger.getInstance();
        Logger l2 = Logger.getInstance();

        System.out.println(l1 == l2); // true
        l1.log("Hello Singleton");
    }
}

特点:

  • 全局只有一个 Logger 实例。
  • 无论程序哪里需要写日志,都用同一个实例,节省资源且统一管理。

2️⃣ Flyweight 示例:文字编辑器中的字符对象

import java.util.HashMap;
import java.util.Map;

// Flyweight 类:Char
class Char {
    private final char symbol; // 内部状态(共享)
    public Char(char symbol) {
        this.symbol = symbol;
    }
    public void display(String color) { // 外部状态(可变)
        System.out.println("Char: " + symbol + " in color " + color);
    }
}

// Flyweight 工厂
class CharFactory {
    private static final Map<Character, Char> pool = new HashMap<>();
    public static Char getChar(char c) {
        pool.putIfAbsent(c, new Char(c)); // 缓存共享对象
        return pool.get(c);
    }
}

public class Main {
    public static void main(String[] args) {
        Char c1 = CharFactory.getChar('A');
        Char c2 = CharFactory.getChar('A'); // 共享 c1
        Char c3 = CharFactory.getChar('B');

        c1.display("red");
        c2.display("blue"); // 外部状态不同,但共享同一个实例
        c3.display("green");

        System.out.println(c1 == c2); // true
        System.out.println(c1 == c3); // false
    }
}

特点:

  • 相同字符只创建一个实例,多次使用直接复用。
  • 外部状态(颜色)不同,不影响共享的内部状态。
  • 节省内存,尤其适合大量重复对象。

3️⃣ 对比总结

特性 Singleton Flyweight
对象数量 1 个全局实例 多个对象,但相同内部状态共享
是否共享 全局共享 部分共享(内部状态)
使用方式 Logger.getInstance() CharFactory.getChar('A')
核心目的 保证全局唯一性 节省内存、重复对象复用
外部状态 可变 由客户端传入,不影响共享对象

💡 直观理解:

  • Singleton:想象办公室里只有 一个打印机,大家都用它。
  • Flyweight:想象办公室里有很多员工在用相同型号的 ,笔的型号共享,但每个人写的颜色不同。
posted @ 2025-09-06 15:16  AlphaGeek  阅读(14)  评论(0)    收藏  举报