享元模式(Flyweight Pattern)是一种结构型设计模式,它主要通过共享对象来降低系统中对象的数量,从而减少内存占用和提高程序性能。这听起来有点像单例模式,但它们在实现和用途上有很大的区别。享元模式的核心是把一个对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的;然后通过共享不变的部分,达到减少对象数量并节约内存的目的。享元模式的本质是缓存共享对象,降低内存消耗。
享元接口:定义了享元对象的公共方法,这些方法可以操作享元对象的外部状态,外部状态一般作为方法参数传入。
具体享元类:实现享元接口,完成具体的对象操作。内部状态作为成员属性存在,一旦初始化完成就不再改变,不对外提供setter方法。
享元工厂:负责创建和管理享元对象。当客户端请求一个享元对象时,享元工厂会检查是否有已经创建的享元对象,如果有,则直接返回;如果没有,则创建一个新的享元对象并加入到享元池中。
复杂度增加:享元模式需要将对象的状态进行拆分,引入了内部状态和外部状态的管理,增加了系统的复杂性。
线程安全问题:由于享元对象是共享的,因此在多线程环境下,对享元对象的操作需要考虑线程安全。
假设我们需要在一个坐标图纸上,绘制100个固定半径的圆,圆分为三种颜色。常规方法,我们定义一个圆,里面包含半径,颜色,横坐标,纵坐标四个属性,再定义一个绘制的方法draw(),然后我们创建100个对象,调用draw()方法就可以实现。但是如果我们用享元模式实现,仅需创建3个对象即可,其关键就是将颜色和半径作为内部状态共享,将坐标作为外部状态分离出来。
步骤1:创建享元接口Shape
public interface Shape {
//x,y表示坐标
void draw(int x,int y);
}
步骤2:创建具体享元类Circle
public class Circle implements Shape{
private String color;
private int radius;
public Circle(String color){
this.color = color;
this.radius = 10;
}
@Override
public void draw(int x,int y) {
System.out.println("--在坐标("+x+","+y+")处画圆: [颜色 : " + color
+", 半径 :" + radius+"]");
}
}
步骤3:创建享元工厂ShapeFactory
public class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle)circleMap.get(color);
System.out.println("从缓存中获取"+color+"色的圆");
if(circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("缓存中不存在,先创建"+color+"色的圆,并放入缓存中");
}
return circle;
}
}
步骤4:客户端测试
public class Client {
private static final String colors[] = { "Red", "Green", "Blue" };
public static void main(String[] args) {
for(int i=0; i < 10; ++i) {
Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor());
circle.draw(getRandomX(),getRandomY());
}
}
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);
}
}
测试结果
享元模式是一种非常实用的设计模式,它的思想很简单,就是把一些可以共享的对象只创建一份,放入到缓存池中,供业务方引用。这样做可以大大减少对象的创建开销,减少内存中相似或相同对象的数量,减少内存占用。在java源码中也有很多享元模式的思想体现,如String的字符串常量池,Integer包装类中的IntegerCach,以及各种连接池,线程池等技术。我们在日常开发过程中,可以结合上下文场景,灵活运用享元模式,但需要注意线程安全性和共享池的管理。
最后,希望这篇文章能对大家有所帮助,如果你喜欢这篇文章,不妨点个赞和分享给你的朋友们。欢迎大家在评论区留言交流,我们下次再见!