享元模式(Flyweight Pattern)
# 享元模式(Flyweight Pattern)
- 享元模式 (Flyweight Pattern),运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。对象结构型
- 在享元模式中可以共享的相同内容称为内部状态 (IntrinsicState),而那些需要外部环境来设置的不能共享的内容称为外部状态 (Extrinsic State),由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。
- 在享元模式中通常会出现工厂模式,需要创建 ** 一个享元工厂来负责维护一个享元池 (Flyweight Pool)** 用于存储具有相同内部状态的享元对象。

Flyweight: 抽象享元类 Connection
@Data
public abstract class AbstractWaitressFlyweight {
boolean canService = true; // 是否能服务
// 正在服务。。。 享元的不可共享属性留给外部进行改变的接口
abstract void service();
// 服务完成。。。 享元的不可共享属性留给外部进行改变的接口
abstract void end();
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
ConcreteFlyweight: 具体享元类 ConnectionImpl(user,pwd,url)
@AllArgsConstructor
public class BeautifulWaitress extends AbstractWaitressFlyweight {
String id; // 工号
String name; // 名字
int age;// 年龄
// 以上都是不变的属性
@Override
void service() {
System.out.println("工号:" + id + ";" + name + " " + age + "正在服务...");
// 改变外部的状态
canService = false;
}
@Override
void end() {
System.out.println("工号:" + id + ";" + name + " " + age + "服务结束...");
// 改变外部的状态
canService = true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FlyweightFactory: 享元工厂类;简单工厂,产品就一个 Connection
public class ZuDao {
private static Map<String, AbstractWaitressFlyweight> pool = new HashMap<>();
// 享元,池子中有对象
static {
BeautifulWaitress waitress = new BeautifulWaitress("1234", "小芳", 18);
BeautifulWaitress waitress2 = new BeautifulWaitress("9527", "小梅", 19);
pool.put(waitress.id, waitress);
pool.put(waitress2.id, waitress2);
}
public void addWaitress(AbstractWaitressFlyweight waitressFlyweight) {
pool.put(UUID.randomUUID().toString(), waitressFlyweight);
}
public static AbstractWaitressFlyweight getWaitress(String name) {
AbstractWaitressFlyweight flyweight = pool.get(name);
if (flyweight == null) {
Collection<AbstractWaitressFlyweight> values = pool.values();
for (AbstractWaitressFlyweight value : values) {
// 当前共享对象是否能进行服务
if (value.isCanService()) {
return value;
}
}
return null;
}
return flyweight;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
测试
public class MainTest {
public static void main(String[] args) {
AbstractWaitressFlyweight waitress = ZuDao.getWaitress("");
waitress.service();
System.out.println(waitress);
AbstractWaitressFlyweight waitress1 = ZuDao.getWaitress("");
waitress1.service();
System.out.println(waitress1);
AbstractWaitressFlyweight waitress2 = ZuDao.getWaitress("");
// 没有可用服务元 返回null
System.out.println(waitress2);
// 有一个服务员结束服务 再进行获取服务员
waitress1.end();
waitress2 = ZuDao.getWaitress("");
System.out.println(waitress2);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 应用场景
什么场景用到?
- 典型的代表:数据库连接池
- 所有的池化技术
- 享元和原型模式有什么区别?享元是预先准备好的对象进行复用,原型没法确定预先有哪些
Integer 中的享元模式

public static void main(String[] args) {
// TODOAuto-generated method stub
//如果 Integer.valueOf(x) x 在 -128 --- 127 直接,就是使用享元模式返回,如果不在
//范围类,则仍然 new
//小结:
//1. 在 valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的 Integer(new), 否则,就
直接从 缓存池返回
//2. valueOf 方法,就使用到享元模式
//3. 如果使用 valueOf 方法得到一个 Integer 实例,范围在 -128 - 127 ,执行速度比 new 快
Integer x = Integer.valueOf(127); // 得到 x 实例,类型 Integer
Integer y = new Integer(127); // 得到 y 实例,类型 Integer
Integer z = Integer.valueOf(127);//..
Integer w = new Integer(127);
System.out.println(x.equals(y)); // 大小,true
System.out.println(x == y ); // false
System.out.println(x == z ); // true
System.out.println(w == x ); // false
System.out.println(w == y ); // false
Integer x1 = Integer.valueOf(200);
Integer x2 = Integer.valueOf(200);
System.out.println("x1==x2" + (x1 == x2)); // false
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 注意事项和细节
- 在享元模式这样理解,“享” 就表示共享,“元” 表示对象
- 系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
- 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用 HashMap/HashTable 存储
- 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
- 享元模式提高了 系统的复杂度。需要分离出 内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.
- 使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
- 享元模式经典的应用场景是需要缓冲池的场景,比如 String 常量池、数据库连接池
编辑 (opens new window)
上次更新: 2023/12/13, 06:06:02