文章目录
- 引言
- 享元模式简介
- 定义与用途
- 实现方式
- 使用场景
- 优势与劣势
- 在Java中的应用
- 享元模式在Spring中的应用
- 画图示例
- 代码地址
引言
想象一下,您正在开发一个游戏,游戏中有成千上万的树木和建筑。如果每个对象都独立存储它的所有数据,将会占用大量的内存资源。享元模式提供了一种优化的解决方案,它通过共享相似对象的共有部分,减少内存的使用,同时保持独立对象的特性。
享元模式简介
定义与用途
享元模式(Flyweight Pattern)是一种结构型设计模式,用于减少创建大量相似对象的内存开销。它通过共享相似对象的共有状态,减少资源消耗,特别是在需要大量对象的情况下。
实现方式
实现享元模式通常包括以下几个关键组件:
- 享元接口(Flyweight): 定义了享元对象的共有方法。
- 具体享元(Concrete Flyweight): 实现享元接口,存储内部状态。
- 享元工厂(Flyweight Factory): 创建并管理享元对象,确保合理地共享对象。
使用场景
享元模式适用于以下场景:
- 当一个应用程序需要大量的相似对象时,可以使用享元模式来减少内存消耗。
- 当对象的大多数状态可以被外部化时,享元模式可以有效地共享数据。
- 当需要细粒度对象来表示数据时,而且这些数据可以被共享。
例如:
- 游戏开发中的环境设计:如树木、草、石头等,可以共享相同的模型和纹理。
- 文本处理程序中的字符处理:字符实例可以被共享,以减少内存占用。
- 用户界面中的控件和图标:在整个应用中可以共享相同的图标或控件实例。
优势与劣势
- 优势
减少内存消耗: 通过共享相似对象,显著减少内存占用。
提高性能: 减少对象创建和销毁的开销,提升应用性能。 - 劣势
增加系统复杂性: 需要维护共享对象的状态,可能增加系统的复杂性。
外部状态管理: 享元对象的外部状态需要由客户端代码维护,可能增加客户端的负担。
在Java中的应用
在Java中,String常量池是享元模式的一个经典例子。Java虚拟机(JVM)中的String常量池存储了所有的字符串字面量。这些字符串字面量是共享的,从而节约了内存。
享元模式在Spring中的应用
在Spring框架中,享元模式的应用并不像一些其他设计模式那样显而易见,但它确实在一些关键部分发挥作用,特别是在优化性能和资源管理方面。以下是一些示例:
Spring Bean的作用域管理:在Spring框架中,Bean的作用域可以被定义为单例(Singleton),这实际上是享元模式的一种应用。在单例模式下,Spring容器为每个Bean定义创建一个唯一的实例,并在整个容器中共享这个实例。这种方式减少了对象的创建,从而节约资源和提高效率。
Spring Security的权限缓存:在Spring Security中,权限信息经常被缓存以提高性能。这些权限对象的实例在需要时被创建,并在多个上下文中共享,这就是一种享元模式的实现。通过共享相同的权限对象实例,Spring Security减少了对象的创建和内存占用。
资源池的实现:在Spring中,资源如数据库连接和线程池常常使用享元模式进行管理。这些资源被创建并存储在池中,当需要时可以被多个客户端共享和重用。这样的资源共享减少了资源的频繁创建和销毁,优化了性能。
缓存机制:Spring框架提供了缓存抽象,可以通过缓存共享经常访问的数据,减少对外部系统(如数据库)的访问。这种缓存策略的背后思想与享元模式相似,即重用已有对象来减少资源消耗和提高效率。
通过这些应用,Spring框架有效地实现了享元模式的核心思想:共享和重用对象,以减少资源消耗和提高应用性能。
画图示例
步骤 1: 创建图形一个接口。
public interface Shape {
void draw();
}
步骤 2: 实现具体类,创建了 Circle 类,实现了 Shape 接口。这个类包含圆的属性,如颜色、坐标和半径。
public class Circle implements Shape {
private String color;
private int x;
private int y;
private int radius;
public Circle(String color){
this.color = color;
}
// 省略了设置 x, y, radius 的方法
@Override
public void draw() {
System.out.println("绘制圆形:[颜色 : " + color + ", x : " + x + ", y :" + y + ", 半径 :" + radius);
}
}
步骤 3: 创建工厂类
ShapeFactory 类用于基于给定的信息(如颜色)生成 Circle 对象。它内部维护了一个 HashMap,用于缓存已经创建的 Circle 对象。
import java.util.HashMap;
public class ShapeFactory {
private static final HashMap<String, Circle> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = circleMap.get(color);
if(circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("创建颜色为 " + color + " 的圆形");
}
return circle;
}
}
步骤 4: 使用工厂类
FlyweightPatternDemo 类演示了如何使用 ShapeFactory 来获取特定颜色的 Circle 实例。它演示了如何有效地重用已经创建的对象,而不是每次都创建新对象。
public class FlyweightPatternDemo {
private static final String[] colors = { "红色", "绿色", "蓝色", "白色", "黑色" };
public static void main(String[] args) {
for(int i = 0; i < 20; ++i) {
Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int)(Math.random() * colors.length)];
}
private static int getRandomX() {
return (int)(Math.random() * 100);
}
private static int getRandomY() {
return (int)(Math.random() * 100);
}
}
这个示例中,ShapeFactory 充当享元工厂,管理 Circle 对象的创建和缓存。当请求特定颜色的圆时,工厂首先检查是否已经创建了该颜色的圆。如果已存在,则重用该对象;如果不存在,才创建新的圆,并将其存储在哈希表中以供将来重用。这种方式显著减少了对象创建的数量,从而优化了内存使用和性能。
代码地址
23种设计模式相关代码后续会逐步提交到github上,方便学习,欢迎指点:
代码地址
https://github.com/RuofeiSun/lf-23Pattern