【HeadFirst系列之HeadFirst设计模式】第18天之蝇量模式(Flyweight Pattern):优化资源的秘密武器

蝇量模式(Flyweight Pattern):优化资源的秘密武器

在软件开发中,当系统需要创建大量相似对象时,内存占用和性能问题就会浮出水面。《Head First 设计模式》介绍了 蝇量模式(Flyweight Pattern) ,这是一种优化对象复用的结构型设计模式,旨在减少对象创建的内存开销。本文将深入剖析蝇量模式的核心内容,并结合 JDK 和 Spring 框架中的应用 进行实践探讨。


1. 遇到什么问题?

场景:大量重复对象导致内存占用过高

假设我们要开发一款绘图应用,支持绘制大量的"树木"对象,每棵树都有以下属性:

  • 内部状态(Intrinsic State): 颜色、树种、形状等,所有相同种类的树共享相同的属性。
  • 外部状态(Extrinsic State): 坐标(X、Y)、环境信息等,每棵树的位置信息不同。

如果每棵树都是一个独立的对象,并存储所有属性,那么 上千棵树可能会占用大量内存,导致应用性能下降。


2. 怎么解决?

引入蝇量模式

蝇量模式的核心思想是 将共享部分(内部状态)抽取出来,避免不必要的重复存储,而唯一的不同信息(外部状态)由客户端传递。

这样,相同种类的树可以复用同一个对象,而位置信息等变化属性则由外部提供。

核心设计

  • Flyweight(蝇量对象): 共享的对象,包含内部状态。
  • FlyweightFactory(工厂类): 负责管理和复用 Flyweight 对象,确保相同的对象不会重复创建。
  • Client(客户端): 负责存储外部状态,并在需要时从工厂获取共享对象。

3. 代码实现

我们以"绘制森林中的树木"为例,使用 Java 代码 实现蝇量模式:

(1)创建 Flyweight 接口

java 复制代码
// 蝇量接口,定义共享方法
public interface TreeFlyweight {
    void display(int x, int y); // 外部状态由方法参数传入
}

(2)具体的 Flyweight 类

java 复制代码
// 具体的树对象,包含共享的内部状态
public class Tree implements TreeFlyweight {
    private final String type;  // 树的种类
    private final String color; // 颜色
    private final String texture; // 纹理

    public Tree(String type, String color, String texture) {
        this.type = type;
        this.color = color;
        this.texture = texture;
    }

    @Override
    public void display(int x, int y) {
        System.out.println("绘制 " + type + " 树,颜色: " + color + ",纹理: " + texture + ",位置: (" + x + "," + y + ")");
    }
}

(3)创建 FlyweightFactory 负责管理共享对象

java 复制代码
import java.util.HashMap;
import java.util.Map;

// 工厂类,管理共享对象
public class TreeFactory {
    private static final Map<String, TreeFlyweight> treeCache = new HashMap<>();

    public static TreeFlyweight getTree(String type, String color, String texture) {
        String key = type + "-" + color + "-" + texture;
        if (!treeCache.containsKey(key)) {
            treeCache.put(key, new Tree(type, color, texture));
            System.out.println("创建新树对象:" + key);
        } else {
            System.out.println("复用已有树对象:" + key);
        }
        return treeCache.get(key);
    }
}

(4)客户端使用 Flyweight

java 复制代码
public class FlyweightPatternDemo {
    public static void main(String[] args) {
        // 创建多个树对象,但只会创建必要的不同种类树,其他复用
        TreeFlyweight tree1 = TreeFactory.getTree("Oak", "Green", "Smooth");
        tree1.display(10, 20);

        TreeFlyweight tree2 = TreeFactory.getTree("Oak", "Green", "Smooth");
        tree2.display(30, 40);

        TreeFlyweight tree3 = TreeFactory.getTree("Pine", "Dark Green", "Rough");
        tree3.display(50, 60);
    }
}

(5)运行结果

plaintext 复制代码
创建新树对象:Oak-Green-Smooth
绘制 Oak 树,颜色: Green,纹理: Smooth,位置: (10,20)
复用已有树对象:Oak-Green-Smooth
绘制 Oak 树,颜色: Green,纹理: Smooth,位置: (30,40)
创建新树对象:Pine-Dark Green-Rough
绘制 Pine 树,颜色: Dark Green,纹理: Rough,位置: (50,60)

说明:

  • Oak-Green-Smooth 只创建了一次,后续直接复用。
  • Pine-Dark Green-Rough 由于是新的类型,所以创建新的对象。

4. 蝇量模式的应用场景

(1)JDK 中的应用

  • Java String 常量池:

    java 复制代码
    String s1 = "hello";
    String s2 = "hello";
    System.out.println(s1 == s2); // true(字符串池复用)

    JDK 采用 字符串池(String Pool) 机制复用字符串对象,避免重复创建。

  • Integer.valueOf() 复用小整数对象(-128 ~ 127):

    java 复制代码
    Integer a = Integer.valueOf(127);
    Integer b = Integer.valueOf(127);
    System.out.println(a == b); // true(缓存复用)

    JDK 通过 IntegerCache 复用小整数,减少对象创建。

(2)Spring 框架中的应用

  • Spring Bean 作用域(Scope)中的单例模式

    • 默认情况下,Spring 管理的 Bean 是 单例模式,所有请求共享同一个 Bean 实例,相当于全局的蝇量对象。

    • 例如:

      java 复制代码
      @Component
      @Scope("singleton") // 只创建一个实例
      public class SingletonService { }
  • Spring MVC 中的视图对象复用

    • 视图解析器会复用 View 对象,避免重复创建。

5. 蝇量模式的优缺点

优点

减少内存占用 :通过对象共享,降低内存消耗。

提高性能 :避免重复创建对象,优化资源利用。

适用于大量重复对象的场景:如缓存、池化对象等。

缺点

引入了额外的管理逻辑 :需要管理对象池和工厂。

可能导致线程安全问题 :共享对象需要考虑并发访问。

不适用于对象状态差异很大的场景:如果对象的外部状态过多,模式的优势就会减少。


6. 总结

  • 蝇量模式通过共享对象,减少系统资源开销,优化性能。
  • 适用于大量相似对象的场景,如缓存、池化技术。
  • JDK 和 Spring 中广泛使用,如 String 常量池、IntegerCache、Spring 单例 Bean。
  • 在使用时,需要权衡管理复杂度和性能优化的收益。

如果你的系统面临大量重复对象的内存压力,不妨考虑蝇量模式来优化! 🚀

相关推荐
JinSo18 分钟前
国际化探索:颗粒化方案
前端·javascript·设计模式
JuicyActiveGilbert1 小时前
【C++设计模式】第十六篇:迭代器模式(Iterator)
c++·设计模式·迭代器模式
kkkkatoq2 小时前
设计模式 一、软件设计原则
设计模式
DemonAvenger16 小时前
深入Go并发编程:Goroutine性能调优与实战技巧全解析
设计模式·架构·go
啾啾Fun17 小时前
[Java基础-线程篇]7_线程设计模式与总结
java·开发语言·设计模式
Dontla19 小时前
C++设计模式总结
开发语言·c++·设计模式
JuicyActiveGilbert19 小时前
【C++设计模式】第十篇:外观模式(Facade)
c++·设计模式·外观模式
yuanpan19 小时前
23种设计模式之《装饰器模式(Decorator)》在c#中的应用及理解
设计模式·c#·装饰器模式
程序员云帆哥19 小时前
【玩转23种Java设计模式】结构型模式篇:组合模式
java·设计模式·组合模式
techzhi19 小时前
设计模式-原型模式
设计模式·原型模式