目录
一、享元模式是什么?
享元模式,英文名为 Flyweight Pattern,是一种结构型设计模式 。它的核心目的非常简单直接,就是减少对象的创建数量,从而降低内存占用,提升系统性能。如果用一个通俗的比喻来解释,享元模式就像是共享单车系统。在一个城市里,有成千上万个用户可能需要使用单车,但我们并不需要为每个用户都单独生产一辆单车。相反,共享单车公司会投放一定数量的单车,这些单车被多个用户共享使用。当一个用户使用完后,另一个用户可以接着使用。
在软件系统中,当我们遇到需要创建大量相似对象的情况时,就可以考虑使用享元模式。比如在一个图形绘制系统中,如果要绘制 1000 个红色的圆形,每个圆形的半径、颜色等属性都相同,只是位置不同。按照常规做法,我们可能会创建 1000 个不同的圆形对象,每个对象都包含相同的半径和颜色信息,这样会占用大量的内存空间。但使用享元模式,我们可以只创建一个代表红色圆形的对象,将它的半径和颜色等不变的属性(也就是内部状态)共享起来,而对于每个圆形不同的位置信息(也就是外部状态),则在使用时通过参数传递进去。这样,无论需要绘制多少个红色圆形,我们都只需要使用这一个共享的对象,大大减少了内存的占用。
二、享元模式的结构与角色
了解了享元模式的基本概念后,我们来深入分析一下它的结构和各个角色 。享元模式主要包含以下四个角色:抽象享元角色、具体享元角色、非共享具体享元角色和享元工厂角色。下面我将结合前面图形绘制系统的例子,为大家详细介绍每个角色的作用和职责。
2.1 抽象享元角色(Flyweight)
抽象享元角色是所有具体享元类的父类或接口,它定义了具体享元类必须实现的方法,为具体享元角色规定了统一的行为规范。在图形绘制系统中,我们可以定义一个抽象的图形接口Shape作为抽象享元角色,它包含一个绘制方法draw,具体的图形类(如圆形、正方形等)都需要实现这个接口。
public interface Shape {
void draw(String position);
}
在这个接口中,draw方法接收一个表示位置的参数position,这个参数就是外部状态,它会根据不同的绘制需求而变化。通过这种方式,抽象享元角色将外部状态以参数的形式传入具体享元角色,使得具体享元角色可以在不同的外部状态下执行相同的操作。
2.2 具体享元角色(ConcreteFlyweight)
具体享元角色是实现了抽象享元角色接口的类,它包含了内部状态,并且这些内部状态是可以共享的。在图形绘制系统中,Circle类就是一个具体享元角色,它实现了Shape接口,并且包含了一个表示颜色的内部状态color。
public class Circle implements Shape {
private String color;
public Circle(String color) {
this.color = color;
}
@Override
public void draw(String position) {
System.out.println("绘制一个颜色为 " + color + " 的圆形,位置在 " + position);
}
}
在Circle类的构造函数中,我们初始化了颜色这个内部状态。由于颜色对于所有相同颜色的圆形来说是不变的,所以可以将其作为内部状态进行共享。而在draw方法中,我们通过传入的position参数来处理外部状态,实现了在不同位置绘制相同颜色圆形的功能。
2.3 非共享具体享元角色(UnsharedConcreteFlyweight)
并非所有的抽象享元类的子类都需要被共享,那些不能被共享的子类就可以设计为非共享具体享元角色。当需要一个非共享具体享元类的对象时,可以直接通过实例化创建。在某些复杂的图形绘制系统中,可能存在一些特殊的图形,它们的属性和行为与其他图形差异较大,不适合共享。比如一个具有独特动画效果的图形,它的动画逻辑和数据与其他图形不同,这种情况下就可以将其设计为非共享具体享元角色。
public class SpecialShape implements Shape {
private String uniqueProperty;
public SpecialShape(String uniqueProperty) {
this.uniqueProperty = uniqueProperty;