享元模式
# 享元模式
# 一、享元模式
原理:享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。
优缺点
- 1 优点
- 减少内存使用: 通过共享相似对象的公共部分,可以减少内存使用量,特别是当系统中存在大量相似对象时,这种节省可以显著提高性能和减少资源消耗。
- 提高性能: 由于减少了对象的数量,因此在创建和销毁对象时的开销也相应减少,可以提高系统的性能。
- 提高可维护性: 享元模式将对象的状态分为内部状态和外部状态,内部状态是可以共享的,外部状态是不可共享的。这种区分有助于更好地管理对象状态,使得系统更易于维护。
- 支持大规模对象: 享元模式适用于需要大量相似对象的场景,例如图形编辑器中的图元对象,文档编辑器中的字符对象等。
- 2 缺点
- 复杂性增加: 享元模式引入了共享对象池和状态管理机制,这增加了系统的复杂性,使得代码变得更加难以理解和维护。
- 可能引入线程安全问题: 如果享元对象被多个线程同时访问和修改,可能会引入线程安全问题,需要额外的同步机制来保证对象的状态不被破坏。
- 可能引入性能问题: 尽管享元模式可以减少内存使用和对象数量,但在某些情况下,共享对象的创建和管理可能会引入额外的性能开销,特别是在对象的状态转换复杂或者需要频繁修改状态时。
- 对外部状态的管理: 在享元模式中,需要额外的机制来管理外部状态,例如在享元对象外部维护状态信息的数据结构,这增加了系统的复杂性和开发成本。
适用场景
- 大量相似对象: 当系统中存在大量相似对象,并且这些对象的区别主要在于它们的内部状态时,可以使用享元模式来共享这些相似对象的内部状态,减少内存消耗。
- 频繁创建和销毁对象: 如果系统中频繁创建和销毁对象,而且创建对象的开销比较大,可以使用享元模式来缓存已经创建的对象,避免重复创建,提高系统性能。
- 对象数量巨大: 在需要管理大量对象的系统中,例如图形编辑器、文档编辑器等,通过享元模式可以有效地减少对象的数量,减少系统的内存消耗和资源占用。
- 对象的状态可以分为内部状态和外部状态: 如果对象的状态可以分为内部状态和外部状态,并且内部状态可以共享,外部状态可以通过参数传递的方式进行管理,那么可以使用享元模式来提高系统的效率。
- 需要缓存对象以提高性能: 在需要缓存对象以提高系统性能的场景中,可以使用享元模式来缓存对象,避免重复创建和销毁对象,提高系统的响应速度。
# 二、享元模式基本代码
享元接口
interface Shape { void draw(); }
1
2
3具体享元类
class Circle implements Shape { private String color; public Circle(String color) { this.color = color; } @Override public void draw() { System.out.println("Drawing Circle with color: " + color); } }
1
2
3
4
5
6
7
8
9
10
11
12享元工厂类
class ShapeFactory { private static final HashMap<String, Shape> circleMap = new HashMap<>(); // 获取圆形对象 public static Shape getCircle(String color) { Circle circle = (Circle) circleMap.get(color); // 如果不存在该颜色的圆形,则创建一个新的圆形对象并放入享元池中 if (circle == null) { circle = new Circle(color); circleMap.put(color, circle); System.out.println("Creating new Circle with color: " + color); } return circle; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17客户端
public class Main { private static final String[] colors = { "Red", "Green", "Blue", "Yellow", "Black" }; public static void main(String[] args) { // 模拟绘制不同颜色的圆形 for (int i = 0; i < 10; ++i) { String color = colors[(int) (Math.random() * colors.length)]; Shape circle = ShapeFactory.getCircle(color); circle.draw(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
# 三、实现方式
- 将需要改写为享元的类成员变量拆分为两个部分:
- 内在状态: 包含不变的、 可在许多对象中重复使用的数据的成员变量。
- 外在状态: 包含每个对象各自不同的情景数据的成员变量
- 保留类中表示内在状态的成员变量, 并将其属性设置为不可修改。 这些变量仅可在构造函数中获得初始数值。
- 找到所有使用外在状态成员变量的方法, 为在方法中所用的每个成员变量新建一个参数, 并使用该参数代替成员变量。
- 你可以有选择地创建工厂类来管理享元缓存池, 它负责在新建享元时检查已有的享元。 如果选择使用工厂, 客户端就只能通过工厂来请求享元, 它们需要将享元的内在状态作为参数传递给工厂。
- 客户端必须存储和计算外在状态 (情景) 的数值, 因为只有这样才能调用享元对象的方法。 为了使用方便, 外在状态和引用享元的成员变量可以移动到单独的情景类中。
编辑 (opens new window)