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. 核心概念
-
优点:
-
减少内存使用:共享相同内部状态的对象,减少重复对象的创建
-
提高性能:减少对象创建和垃圾回收的开销
-
更好的对象管理:通过工厂类集中管理享元对象
-
-
适用场景:
-
系统中有大量相似对象
-
对象的大部分状态可以外部化
-
需要缓冲池的场景
-
游戏开发中的资源管理(如纹理、模型等)
-