23种设计模式之享元模式
参考资料
- Java设计模式:23种设计模式全面解析(超级详细)
- 韩顺平老师的Java设计模式(图解+框架源码剖析)
- 秦小波老师的《设计模式之禅》
下文如有错漏之处,敬请指正
一、简介
定义
运用共享技术对大量细粒度对象进行复用。
通过共享已经存在的对象、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式中存在以下两种状态:
-
内部状态,指不会随着环境的改变而改变的可共享部分。
-
外部状态,指会随环境改变而改变的不可共享部分。
特点
- 享元模式是一种结构型模式
- 享元模式是池技术的重要实现方式
通用类图
享元模式的主要角色:
-
Flyweight
抽象享元角色
抽象产品类,定义出对象的外部状态和内部状态的接口或实现。
-
ConcreteFlyweight
具体享元角色
具体产品类,实现抽象享元角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允 许的。
-
UnsharableFlyweight
不可共享的享元角色
是不需要要共享的外部对象,它以参数的形式注入具体享元的相关方法中。
-
FlyweightFactory
享元工厂角色
用于构建一个池容器,负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象放到池子并返回。
-
Client
客户端角色
客户端通过享元工厂获取享元对象。
优点
相同对象只保存一份,降低了系统中重复对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
缺点
-
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
-
读取享元模式的外部状态会使得运行时间稍微变长。
应用场景
- 常用于系统底层开发,解决系统的性能问题。如数据库连接池、String常量池、缓冲池。
- 系统中存在大量的相似对象。
- Spring的单例池
- JDK中的Integer.valueOf()
二、享元模式
需求:
模拟一个文件夹里有三个共享资源,每个用户都可以获取资源,每个用户都有一个id,在获取资源时将会记录用户的id。
抽象享元角色:
package flyweight;
public abstract class Flyweight {
// 内部状态(本程序未使用内部状态)
private String intrinsic;
// 外部状态
// 在程序开发中,确认只需要一次赋值的属性则设置为final类型,避免无意修改导致逻辑混乱
protected final String key;
// 要求享元角色必须接受外部状态
public Flyweight(String key) {
this.key = key;
}
public String getKey() {
return key;
}
// 定义业务操作
// 获取资源
public abstract void getResource(UnsharableFlyweight unsharableFlyweight);
}
具体享元角色:
package flyweight;
public class ConcreteFlyweight extends Flyweight{
public ConcreteFlyweight(String key) {
super(key);
}
@Override
public void getResource(UnsharableFlyweight unsharableFlyweight) {
System.out.println("id为"+unsharableFlyweight.getId()+"的用户正在获取资源"+this.getKey()+"……");
}
}
不可共享的享元角色:
package flyweight;
public class UnsharableFlyweight {
// 用户id
private String id;
public UnsharableFlyweight(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
享元工厂:
package flyweight;
import java.util.HashMap;
import java.util.Map;
public class FlyweightFactory {
// 定义一个池容器
private static Map<String, Flyweight> pool = new HashMap<>();
// 享元工厂
public static Flyweight getResource(String key) {
// 需要返回的对象
Flyweight resource = null;
// 在池中没有该对象
if (pool.containsKey(key)) {
resource=pool.get(key);
System.out.println("资源" + key + "已被创建,可以获取");
} else {
// 根据外部状态创建享元对象
resource = new ConcreteFlyweight(key);
System.out.println("资源" + key + "正在创建……");
pool.put(key, resource);
}
return resource;
}
}
Client:
package flyweight;
public class Client {
public static void main(String[] args) {
// 获取3个文件资源
Flyweight resource1 = FlyweightFactory.getResource("1");
Flyweight resource2 = FlyweightFactory.getResource("2");
Flyweight resource3=FlyweightFactory.getResource("3");
resource3=FlyweightFactory.getResource("3");
// 定义3个用户
UnsharableFlyweight user1 = new UnsharableFlyweight("A");
UnsharableFlyweight user2 = new UnsharableFlyweight("B");
UnsharableFlyweight user3 = new UnsharableFlyweight("C");
// 获取指定的资源
resource1.getResource(user1);
resource2.getResource(user1);
resource3.getResource(user2);
resource3.getResource(user3);
/**
* 输出结果:
* 资源1正在创建……
* 资源2正在创建……
* 资源3正在创建……
* 资源3已被创建,可以获取
* id为A的用户正在获取资源1……
* id为A的用户正在获取资源2……
* id为B的用户正在获取资源3……
* id为C的用户正在获取资源3……
*/
}
}
三、总结
享元模式是一个非常简单的模式,它可以大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能。