结构型-享元模式

1. 项目结构

bash 复制代码
	flyweight-demo/
	├── src/
	│   ├── main/
	│   │   ├── java/
	│   │   │   └── com/
	│   │   │       └── flyweight/
	│   │   │           ├── demo/
	│   │   │           │   ├── FlyweightPatternDemo.java
	│   │   │           │   └── Forest.java
	│   │   │           ├── factory/
	│   │   │           │   └── TreeFactory.java
	│   │   │           ├── model/
	│   │   │           │   ├── Tree.java
	│   │   │           │   ├── TreeType.java
	│   │   │           │   └── TreeContext.java
	│   │   │           └── utils/
	│   │   │               └── ColorType.java
	│   │   └── resources/
	│   └── test/
	│       └── java/
	├── pom.xml
	└── README.md

Maven 配置文件 (pom.xml)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.flyweight</groupId>
    <artifactId>flyweight-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>Flyweight Pattern Demo</name>
    <description>Design Pattern - Flyweight Pattern Demo</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.13.2</junit.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 代码实现

ColorType.java

java 复制代码
package com.flyweight.utils;

/**
 * 颜色枚举类
 */
public enum ColorType {
    GREEN("绿色"),
    BROWN("棕色"),
    RED("红色"),
    YELLOW("黄色"),
    ORANGE("橙色");
    
    private final String colorName;
    
    ColorType(String colorName) {
        this.colorName = colorName;
    }
    
    public String getColorName() {
        return colorName;
    }
    
    @Override
    public String toString() {
        return colorName;
    }
}

TreeType.java

java 复制代码
package com.flyweight.model;

import com.flyweight.utils.ColorType;

/**
 * 享元对象 - 树木类型(内部状态,可共享)
 */
public class TreeType {
    private final String name;        // 树木名称
    private final ColorType color;    // 树木颜色
    private final String texture;     // 树木纹理
    
    public TreeType(String name, ColorType color, String texture) {
        this.name = name;
        this.color = color;
        this.texture = texture;
    }
    
    public void draw(int x, int y) {
        System.out.printf("绘制树木: %s, 颜色: %s, 纹理: %s, 位置: (%d, %d)%n",
                name, color.getColorName(), texture, x, y);
    }
    
    // Getters
    public String getName() {
        return name;
    }
    
    public ColorType getColor() {
        return color;
    }
    
    public String getTexture() {
        return texture;
    }
    
    @Override
    public String toString() {
        return "TreeType{" +
                "name='" + name + '\'' +
                ", color=" + color.getColorName() +
                ", texture='" + texture + '\'' +
                '}';
    }
}

TreeContext.java

java 复制代码
package com.flyweight.model;

/**
 * 外部状态 - 树木位置(不可共享,每个对象独立)
 */
public class TreeContext {
    private final int x;  // X坐标
    private final int y;  // Y坐标
    
    public TreeContext(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    public int getX() {
        return x;
    }
    
    public int getY() {
        return y;
    }
    
    @Override
    public String toString() {
        return "TreeContext{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}

Tree.java

java 复制代码
package com.flyweight.model;

/**
 * 树木类 - 包含享元对象和外部状态
 */
public class Tree {
    private final TreeType treeType;      // 享元对象(内部状态)
    private final TreeContext context;    // 外部状态
    
    public Tree(TreeType treeType, int x, int y) {
        this.treeType = treeType;
        this.context = new TreeContext(x, y);
    }
    
    public void draw() {
        treeType.draw(context.getX(), context.getY());
    }
    
    public TreeType getTreeType() {
        return treeType;
    }
    
    public TreeContext getContext() {
        return context;
    }
    
    @Override
    public String toString() {
        return "Tree{" +
                "treeType=" + treeType +
                ", context=" + context +
                '}';
    }
}

TreeFactory.java

java 复制代码
package com.flyweight.factory;

import com.flyweight.model.TreeType;
import com.flyweight.utils.ColorType;

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

/**
 * 享元工厂 - 创建和管理享元对象
 */
public class TreeFactory {
    // 使用HashMap作为享元池
    private static final Map<String, TreeType> treeTypeCache = new HashMap<>();
    
    /**
     * 获取树木类型(享元对象)
     * 如果池中存在则直接返回,否则创建新的并放入池中
     */
    public static TreeType getTreeType(String name, ColorType color, String texture) {
        String key = getKey(name, color, texture);
        
        TreeType treeType = treeTypeCache.get(key);
        
        if (treeType == null) {
            System.out.println("创建新的TreeType: " + key);
            treeType = new TreeType(name, color, texture);
            treeTypeCache.put(key, treeType);
        } else {
            System.out.println("从缓存中获取TreeType: " + key);
        }
        
        return treeType;
    }
    
    /**
     * 生成唯一键
     */
    private static String getKey(String name, ColorType color, String texture) {
        return name + "_" + color.name() + "_" + texture;
    }
    
    /**
     * 获取缓存中的树木类型数量
     */
    public static int getTreeTypeCount() {
        return treeTypeCache.size();
    }
    
    /**
     * 清空缓存(用于测试)
     */
    public static void clearCache() {
        treeTypeCache.clear();
    }
    
    /**
     * 打印缓存中的所有树木类型
     */
    public static void printCache() {
        System.out.println("\n=== 当前享元池中有 " + treeTypeCache.size() + " 种树木类型 ===");
        treeTypeCache.forEach((key, treeType) -> {
            System.out.println("Key: " + key + " -> " + treeType);
        });
        System.out.println("=== 享元池打印结束 ===\n");
    }
}

Forest.java

java 复制代码
package com.flyweight.demo;

import com.flyweight.factory.TreeFactory;
import com.flyweight.model.Tree;
import com.flyweight.model.TreeType;
import com.flyweight.utils.ColorType;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 森林 - 使用享元模式创建大量树木
 */
public class Forest {
    private final List<Tree> trees = new ArrayList<>();
    private final Random random = new Random();
    
    /**
     * 种植一棵树
     */
    public void plantTree(String name, ColorType color, String texture, int x, int y) {
        // 从工厂获取享元对象
        TreeType treeType = TreeFactory.getTreeType(name, color, texture);
        // 创建树木对象
        Tree tree = new Tree(treeType, x, y);
        trees.add(tree);
    }
    
    /**
     * 随机种植多棵树
     */
    public void plantRandomTrees(int count) {
        String[] treeNames = {"松树", "橡树", "枫树", "白杨", "柳树"};
        ColorType[] colors = ColorType.values();
        String[] textures = {"粗糙", "光滑", "有纹理", "条纹状"};
        
        for (int i = 0; i < count; i++) {
            String name = treeNames[random.nextInt(treeNames.length)];
            ColorType color = colors[random.nextInt(colors.length)];
            String texture = textures[random.nextInt(textures.length)];
            int x = random.nextInt(1000);  // 模拟X坐标
            int y = random.nextInt(1000);  // 模拟Y坐标
            
            plantTree(name, color, texture, x, y);
        }
    }
    
    /**
     * 绘制森林中的所有树木
     */
    public void drawForest() {
        System.out.println("\n=== 开始绘制森林 ===");
        System.out.println("森林中共有 " + trees.size() + " 棵树");
        System.out.println("但只使用了 " + TreeFactory.getTreeTypeCount() + " 种树木类型\n");
        
        for (Tree tree : trees) {
            tree.draw();
        }
        
        System.out.println("=== 森林绘制完成 ===\n");
    }
    
    /**
     * 获取森林中的树木数量
     */
    public int getTreeCount() {
        return trees.size();
    }
    
    /**
     * 清空森林
     */
    public void clearForest() {
        trees.clear();
    }
}

FlyweightPatternDemo.java

java 复制代码
package com.flyweight.demo;

import com.flyweight.factory.TreeFactory;
import com.flyweight.utils.ColorType;

/**
 * 享元模式演示类
 */
public class FlyweightPatternDemo {
    public static void main(String[] args) {
        System.out.println("========== 享元模式演示开始 ==========\n");
        
        // 创建森林
        Forest forest = new Forest();
        
        // 演示1:种植相同类型的树木
        System.out.println("演示1:种植相同类型的树木(共享享元对象)");
        System.out.println("----------------------------------------");
        forest.plantTree("松树", ColorType.GREEN, "粗糙", 100, 200);
        forest.plantTree("松树", ColorType.GREEN, "粗糙", 150, 250);
        forest.plantTree("松树", ColorType.GREEN, "粗糙", 200, 300);
        forest.plantTree("松树", ColorType.GREEN, "粗糙", 250, 350);
        
        // 打印缓存状态
        TreeFactory.printCache();
        
        // 演示2:种植不同类型的树木
        System.out.println("演示2:种植不同类型的树木");
        System.out.println("----------------------------------------");
        forest.plantTree("橡树", ColorType.BROWN, "有纹理", 300, 400);
        forest.plantTree("枫树", ColorType.RED, "光滑", 350, 450);
        forest.plantTree("柳树", ColorType.GREEN, "条纹状", 400, 500);
        
        // 打印缓存状态
        TreeFactory.printCache();
        
        // 演示3:种植重复类型的树木
        System.out.println("演示3:种植重复类型的树木");
        System.out.println("----------------------------------------");
        forest.plantTree("松树", ColorType.GREEN, "粗糙", 450, 550);
        forest.plantTree("橡树", ColorType.BROWN, "有纹理", 500, 600);
        
        // 打印缓存状态
        TreeFactory.printCache();
        
        // 绘制森林
        forest.drawForest();
        
        // 演示4:大规模种植(展示内存节省效果)
        System.out.println("演示4:大规模随机种植10000棵树");
        System.out.println("----------------------------------------");
        
        // 清空之前的森林
        forest.clearForest();
        TreeFactory.clearCache();
        
        // 种植10000棵随机树木
        forest.plantRandomTrees(10000);
        
        System.out.println("\n统计信息:");
        System.out.println("总共种植了 " + forest.getTreeCount() + " 棵树");
        System.out.println("但只创建了 " + TreeFactory.getTreeTypeCount() + " 种树木类型");
        System.out.println("内存节省: " + calculateMemorySavings(forest.getTreeCount(), TreeFactory.getTreeTypeCount()) + "%");
        
        System.out.println("\n========== 享元模式演示结束 ==========");
    }
    
    /**
     * 计算内存节省百分比
     */
    private static String calculateMemorySavings(int totalTrees, int treeTypes) {
        if (totalTrees <= treeTypes) {
            return "0%";
        }
        
        // 简单估算:假设每棵树类型对象占用1单位内存
        // 没有享元模式:totalTrees单位内存
        // 使用享元模式:treeTypes + totalTrees(位置信息)单位内存
        // 但位置信息总是需要的,所以我们只比较树木类型部分
        
        double withoutFlyweight = totalTrees * 1.0;  // 假设每棵树都需要一个完整的类型对象
        double withFlyweight = treeTypes * 1.0;      // 使用享元模式后
        
        double savings = (1 - withFlyweight / withoutFlyweight) * 100;
        return String.format("%.2f", savings);
    }
}

FlyweightPatternTest.java

java 复制代码
package com.flyweight.demo;

import com.flyweight.factory.TreeFactory;
import com.flyweight.utils.ColorType;
import org.junit.Test;
import static org.junit.Assert.*;

public class FlyweightPatternTest {
    
    @Test
    public void testTreeFactoryCache() {
        // 清空缓存
        TreeFactory.clearCache();
        
        // 获取相同类型的树木类型
        var type1 = TreeFactory.getTreeType("松树", ColorType.GREEN, "粗糙");
        var type2 = TreeFactory.getTreeType("松树", ColorType.GREEN, "粗糙");
        
        // 验证是同一个对象
        assertSame("相同参数的TreeType应该是同一个对象", type1, type2);
        
        // 验证缓存数量
        assertEquals("缓存中应该只有1个TreeType", 1, TreeFactory.getTreeTypeCount());
    }
    
    @Test
    public void testDifferentTreeTypes() {
        TreeFactory.clearCache();
        
        // 获取不同类型的树木类型
        TreeFactory.getTreeType("松树", ColorType.GREEN, "粗糙");
        TreeFactory.getTreeType("橡树", ColorType.BROWN, "有纹理");
        TreeFactory.getTreeType("枫树", ColorType.RED, "光滑");
        
        // 验证缓存数量
        assertEquals("缓存中应该有3个不同的TreeType", 3, TreeFactory.getTreeTypeCount());
    }
    
    @Test
    public void testForestPlanting() {
        Forest forest = new Forest();
        TreeFactory.clearCache();
        
        // 种植树木
        forest.plantTree("松树", ColorType.GREEN, "粗糙", 10, 20);
        forest.plantTree("松树", ColorType.GREEN, "粗糙", 30, 40);
        forest.plantTree("橡树", ColorType.BROWN, "有纹理", 50, 60);
        
        // 验证
        assertEquals("森林中应该有3棵树", 3, forest.getTreeCount());
        assertEquals("缓存中应该有2种树木类型", 2, TreeFactory.getTreeTypeCount());
    }
}

3. 构建和运行

编译项目

bash 复制代码
mvn clean compile

运行程序

bash 复制代码
mvn exec:java -Dexec.mainClass="com.flyweight.demo.FlyweightPatternDemo"

运行测试

bash 复制代码
mvn test

4. 核心概念

  • 优点:

    • 减少内存使用:共享相同内部状态的对象,减少重复对象的创建

    • 提高性能:减少对象创建和垃圾回收的开销

    • 更好的对象管理:通过工厂类集中管理享元对象

  • 适用场景:

    • 系统中有大量相似对象

    • 对象的大部分状态可以外部化

    • 需要缓冲池的场景

    • 游戏开发中的资源管理(如纹理、模型等)

相关推荐
.简.简.单.单.6 小时前
Design Patterns In Modern C++ 中文版翻译 第十一章 享元模式
c++·设计模式·享元模式
JavaBoy_XJ6 小时前
结构型-代理模式
享元模式
syt_10133 天前
设计模式之-享元模式
javascript·设计模式·享元模式
__万波__15 天前
二十三种设计模式(十一)--享元模式
java·设计模式·享元模式
ZouZou老师17 天前
C++设计模式之享元模式:以家具生产为例
c++·设计模式·享元模式
雨中飘荡的记忆23 天前
设计模式之享元模式详解
java·设计模式·享元模式
Jomurphys23 天前
设计模式 - 享元模式 Flyweight Pattern
android·设计模式·享元模式
明洞日记1 个月前
【设计模式手册011】享元模式 - 共享细粒度对象的高效之道
java·设计模式·享元模式