享元模式是一种减少相似对象创建和销毁的设计模式,通过将对象状态分为不变和可变部分,实现内存节省和性能提升。例如,在线游戏中大量玩家角色可共享相同的不变属性,而每人特有的可变属性则单独存储,享元模式使用享元类存储不变属性,非享元类存储可变属性,并通过享元工厂管理对象的复用和共享。
定义
享元模式是一种对象设计模式,它用于减少大量相似对象(也称为“细粒度对象”)的创建和销毁,从而节省内存和提高性能。
享元模式的基本思想是将对象的内部状态分为两部分:不变部分和可变部分。不变部分包括所有对象共享的相同状态,而可变部分是每个对象特有的状态。通过将不变部分提取出来并存储在享元对象中,可以避免重复创建相同的对象,从而减少内存占用。
举一个简单的例子来说明享元模式的应用场景:假设当前正在开发一个在线游戏,游戏中有大量的玩家角色,每个角色都有相同的名称、等级和经验值等不变属性,但装备、技能等级等是每个角色特有的可变属性,在这种情况下,可以将玩家的不变属性存储在一个享元对象中,然后为每个角色分配一个可变属性的实例,这样,多个角色可以共享同一个享元对象,从而减少内存占用。
享元模式在实现时通常使用两个类:一个享元类(Flyweight)用于存储不变属性,另一个非享元类(Unshared)用于存储可变属性,通过享元工厂(FlyweightFactory)来管理享元对象的创建和销毁,以确保它们能够被正确地复用和共享。
代码案例
以下是一个未使用享元模式的反例代码,这个例子中创建了大量的相似对象,导致消耗大量的内存和性能,如下代码:
// 未使用享元模式的反例代码
public class Circle {
private double x;
private double y;
private double radius;
// 构造函数,每次调用都会创建一个新的Circle对象
public Circle(double x, double y, double radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
// 绘制圆形的方法
public void draw() {
System.out.println("Circle: Draw() [x : " + x + ", y :" + y + ", radius :" + radius);
}
}
// 客户端调用案例
public class Client {
public static void main(String[] args) {
// 创建大量的Circle对象,这将消耗大量的内存和性能
for (int i = 0; i < 10; i++) {
Circle circle = new Circle(0, 0, 1); // 所有的圆都有相同的位置和半径
circle.draw();
}
}
}
上面例子中,尽管所有的Circle
对象都有相同的位置和半径,但仍然为每个对象分配了内存,如果需要创建数百万个这样的对象,那么内存消耗将是巨大的,此外,由于每个对象都需要被垃圾收集器处理,这也可能降低程序的性能。
如果使用享元模式,可以共享相同的Circle
对象,从而大大减少内存消耗和提高性能,在享元模式中,通常会创建一个享元工厂来管理和重用对象,这样,即使需要大量的相似对象,也只需要存储它们的一个实例。
以下是一个使用享元模式的正例代码,在这个例子中,将使用享元模式来减少具有相同属性的Circle
对象的创建,将会创建一个CircleFactory
来管理Circle
对象的创建和重用,如下代码:
// 享元模式中的抽象享元角色
interface Shape {
void draw();
}
// 享元模式中的具体享元角色
class Circle implements Shape {
private final String color;
private final int x;
private final int y;
private final int radius;
// 构造函数私有化,因为外部的类不应该直接实例化这个类,而应该通过工厂来获取实例
private Circle(String color, int x, int y, int radius) {
this.color = color;
this.x = x;
this.y = y;
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Circle: Draw() [Color : " + color + ", x : " + x + ", y :" + y + ", radius :" + radius + "]");
}
// 根据属性创建Circle的静态内部类,作为享元工厂的实现
private static class CircleFactory {
// 使用HashMap存储已经创建的Circle对象,实现重用
private static final Map<String, Circle> circleMap = new HashMap<>();
// 获取Circle对象的方法,如果Map中没有则创建一个新的Circle对象并存入Map中
public static synchronized Circle getCircle(String color, int x, int y, int radius) {
String key = color + x + y + radius; // 将属性和在一起作为key,确保相同的属性可以得到相同的对象
if (!circleMap.containsKey(key)) {
circleMap.put(key, new Circle(color, x, y, radius));
}
return circleMap.get(key); // 返回对应key的Circle对象,如果已经存在则直接重用
}
}
// 提供一个静态方法来获取Circle对象,客户端应该通过这个方法来获取Circle对象而不是直接new
public static Circle getCircle(String color, int x, int y, int radius) {
return CircleFactory.getCircle(color, x, y, radius);
}
}
// 客户端调用案例
public class Client {
public static void main(String[] args) {
// 通过享元工厂获取Circle对象,如果具有相同属性的对象已经存在,则直接重用该对象
for (int i = 0; i < 5; i++) {
Circle circle = Circle.getCircle("Red", 0, 0, 1); // 请求相同属性的圆形对象
circle.draw(); // 调用绘制方法,将看到是同一个对象被重用
}
System.out.println("------------------");
// 请求不同属性的圆形对象,将会创建新的对象实例
Circle anotherCircle = Circle.getCircle("Blue", 1, 1, 2);
anotherCircle.draw(); // 调用绘制方法,将看到是一个新的对象被创建并绘制
}
}
在这个例子中,创建了一个Circle
类来实现Shape
接口,使用了一个私有的静态内部类CircleFactory
来作为享元工厂,它使用一个HashMap
来存储已经创建的Circle
对象,客户端通过调用Circle.getCircle()
方法来获取Circle
对象,如果具有相同属性的对象已经存在于Map中,那么该方法将返回这个已经存在的对象,否则,它将创建一个新的对象并将其添加到Map中,这样,就可以重用具有相同属性的对象,从而减少内存消耗并提高性能。
核心总结
享元模式是一种用于优化性能的设计模式,它通过共享相同或相似对象来减少系统中对象的数量,从而节省内存和提高效率,其优点在于能够显著减少对象创建和销毁带来的开销,特别适用于需要大量相似对象但状态可外部化的场景。然而,享元模式也有缺点,它增加了系统的复杂性,需要额外的逻辑来管理共享对象池,并可能导致状态同步问题。当存在明确需要优化对象数量和内存占用时再考虑使用,同时要仔细设计和管理共享对象池,确保状态的一致性和正确性。
其它应用场景补充
数据库连接池,数据库连接池是享元模式最经典的使用场景之一,在这个场景中,创建数据库连接对象需要消耗大量的资源,而且这些对象的内部状态大部分都是相同的,因此,可以通过享元模式来共享这些对象,减少对象的创建和销毁,提高系统的性能和可扩展性。
线程池,线程池也是享元模式的一个应用场景,线程的创建和销毁需要消耗大量的资源,而且线程的内部状态大部分都是相同的,因此,可以通过享元模式来共享线程对象,避免频繁地创建和销毁线程。
大数据处理,在处理大量数据时,可能会存在大量的重复对象,如图像处理中的像素点、文本处理中的单词等,这些对象可以通过享元模式来减少内存消耗和提高处理速度。
其它场景?
完!
标签:享元,程序员,必知,创建,对象,int,radius,Circle From: https://blog.51cto.com/bytegood/9128034