# 享元模式(Flyweight Pattern)
代码地址
- Github:DesignPattern/src/main/java/com/design/flyweight (opens new window)
- Gitee(码云):DesignPattern/src/main/java/com/design/flyweight (opens new window)
# 1. 介绍
享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式,重用现有的同类对象,如果未找到匹配的对象,则创建新对象
享元模式其实是工厂方法模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能,其实就是 JDK 中的字符串常量池,数据库连接池的应用,池化思想
# 1.1. 特点
- 运用共享技术来有效地支持大量细粒度对象的复用
- 通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率
- 相同对象只要保存一份,降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力
# 1.2. 结构
抽象享元(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入 具体享元(Concrete Flyweight):实现抽象享元角色中所规定的接口 非享元角色(Unsharable Flyweight):是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中 享元工厂(Flyweight Factory):负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象
# 1.3. 优缺点
- 意图:运用共享技术有效地支持大量细粒度的对象
- 主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建
- 何时使用:
- 系统中有大量对象
- 这些对象消耗大量内存
- 这些对象的状态大部分可以外部化
- 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替
- 系统不依赖于这些对象身份,这些对象是不可分辨的
- 如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象
- 关键代码:用 HashMap 存储这些对象
- 优点:大大减少对象的创建,降低系统的内存,使效率提高。
- 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱
- 使用场景:
- 系统有大量相似对象
- 需要缓冲池的场景
- JDK 中的字符串常量池,数据库连接池
- 注意事项:
- 注意划分外部状态和内部状态,否则可能会引起线程安全问题
- 这些类必须有一个工厂对象加以控制
# 1.4. 举例
JAVA
中的String
,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面- 数据库的数据池
- 用
HashMap
存储对象
# 2. 代码
用 HashMap
存储对象的例子,不考虑线程安全先
# 2.1. 抽象享元
/**
* 抽象享元
*
* @author wliduo[i@dolyw.com]
* @date 2022/1/24 14:12
*/
public interface Flyweight {
/**
* 处理方法
*
* @param unsharableFlyweight
* @return void
* @throws
* @author wliduo[i@dolyw.com]
* @date 2022/1/24 14:15
*/
void handle(UnsharableFlyweight unsharableFlyweight);
}
# 2.2. 具体享元
/**
* 具体享元
*
* @author wliduo[i@dolyw.com]
* @date 2022/1/24 14:12
*/
public class ConcreteFlyweight implements Flyweight {
private String key;
public ConcreteFlyweight(String key) {
this.key = key;
}
@Override
public void handle(UnsharableFlyweight unsharableFlyweight) {
System.out.println(this.key + "被调用,非享元信息:" + unsharableFlyweight.getMsg());
}
}
# 2.3. 非享元角色
/**
* 非享元角色
*
* @author wliduo[i@dolyw.com]
* @date 2022/1/24 14:14
*/
public class UnsharableFlyweight {
private String msg;
public UnsharableFlyweight(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
}
# 2.4. 享元工厂
/**
* 享元工厂
*
* @author wliduo[i@dolyw.com]
* @date 2022/1/24 14:15
*/
public class FlyweightFactory {
private Map<String, Flyweight> flyweightMap = new HashMap<>(16);
/**
* 工厂根据key获取Flyweight
*
* @param key
* @return com.design.flyweight.Flyweight
* @throws
* @author wliduo[i@dolyw.com]
* @date 2022/1/24 14:19
*/
public Flyweight getFlyweight(String key) {
Flyweight flyweight = flyweightMap.get(key);
if (flyweight == null) {
flyweight = new ConcreteFlyweight(key);
flyweightMap.put(key, flyweight);
System.out.println(key + "不存在,进行创建");
} else {
System.out.println(key + "存在,成功获取");
}
return flyweight;
}
}
# 2.5. 执行
/**
* 享元模式
*
* @author wliduo[i@dolyw.com]
* @date 2022/1/24 13:44
*/
public class Main {
public static void main(String[] args) {
// 创建工厂
FlyweightFactory flyweightFactory = new FlyweightFactory();
// 使用工厂创建3个apple,2个banana对象
Flyweight apple1 = flyweightFactory.getFlyweight("apple");
Flyweight apple2 = flyweightFactory.getFlyweight("apple");
Flyweight apple3 = flyweightFactory.getFlyweight("apple");
Flyweight banana1 = flyweightFactory.getFlyweight("banana");
Flyweight banana2 = flyweightFactory.getFlyweight("banana");
// 使用不同的对象处理发现使用的其实是同一个
apple1.handle(new UnsharableFlyweight("第1次处理apple"));
apple2.handle(new UnsharableFlyweight("第2次处理apple"));
apple3.handle(new UnsharableFlyweight("第3次处理apple"));
banana1.handle(new UnsharableFlyweight("第1次处理banana"));
banana2.handle(new UnsharableFlyweight("第2次处理banana"));
}
}
输出
apple不存在,进行创建
apple存在,成功获取
apple存在,成功获取
banana不存在,进行创建
banana存在,成功获取
apple被调用,非享元信息:第1次处理apple
apple被调用,非享元信息:第2次处理apple
apple被调用,非享元信息:第3次处理apple
banana被调用,非享元信息:第1次处理banana
banana被调用,非享元信息:第2次处理banana