定义
运用共享技术来有效地支持大量细粒度对象的复用;
它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
适用场景
- 常常应用于系统底层的开发,以便解决系统的性能问题;
- 系统有大量相似对象、需要缓冲池的场景。
优点和缺点
优点
- 减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率;
- 减少内存之外的其他资源占用。
缺点
- 关注内/外部状态、关注线程安全问题
- 使系统、程序逻辑复杂化
结构
享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相似,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态
- 内部状态: 指对象共享出来的信息,存储在享元信息内部,并且不会随环境的改变而改变。
- 外部状态: 指对象得以依赖的一个标记,随环境的改变而改变,不可共享。
主要角色:
抽象享元角色: 是所有的具体享元类的基类,为具体享元规范要求实现的公共接口,非享元的外部状态以参数的形式通过方法传入;
具体享元角色:实现抽象享元角色中所规定的接口;
非享元角色: 是不可共享的外部状态,它以参数的形式注入具体享元的相关方法中;
享元工厂角色: 负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检查系统中是否存在符合要求的享元对象,如果存在则提供给客户,如果不存在的话,则创建一个新的享元对象。
代码实现
我们在工作中,经常有一些需求文档,设计文档,接口文档等等,其实每次的文档格式是基本一样的只是内容不同,所以我们可以把文档模板做成享元对象,只需要每次在里面填充不同的内容即可
首先我们创建非享元角色,文档内容,就算模板相同,每个模板的具体内容也是不同,属于外部状态
1 2 3 4 5 6 7 8 9 10 11 12
| public class DocContent {
private String content;
public DocContent(String content) { this.content = content; }
public String getContent() { return content; } }
|
创建抽象享元角色
1 2 3
| public interface Template { void createDoc(DocContent content); }
|
具体享元角色,其中的templateName
字段就是内部状态,每个模板的模板名称是相同的
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class DocTemplate implements Template{
private String templateName;
public DocTemplate(String templateName) { this.templateName = templateName; }
@Override public void createDoc(DocContent content) { System.out.println("当前模板是:"+templateName+" 内容是:"+content.getContent()); } }
|
创建享元工厂,这里是使用HashMap
来做享元对象的存储,HashMap
属于非线程安全的,所以如果是在并发的情况下,可能会有并发问题。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class DocFactory {
private final Map<String,DocTemplate> TEMPLATE_MAP = new HashMap<>(); public Template getTemplate(String templateName){ if (!TEMPLATE_MAP.containsKey(templateName)){ System.out.print("新建模板:"+templateName+" "); TEMPLATE_MAP.put(templateName,new DocTemplate(templateName)); } return TEMPLATE_MAP.get(templateName); }
}
|
具体调用,这里使用随机数连续10次调用来查看输出内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Test {
private static final String[] templateNames = {"需求文档","接口文档","设计文档","简历"};
public static void main(String[] args) { DocFactory factory = new DocFactory();
for (int i = 0; i < 10; i++) { String templateName = templateNames[(int) (Math.random() * templateNames.length)]; Template template = factory.getTemplate(templateName); template.createDoc(new DocContent(templateName+"的内容是"+i+".....")); } } }
|
输出结果,从结果中可以看出,每一种模板在第一次使用的时候会创建新的,但是后续再次调用的时候就不会再新创建对象,而模板的内容每次调用都是不一样的
1 2 3 4 5 6 7 8 9 10
| 新建模板: 接口文档 当前模板是: 接口文档 内容是: 接口文档的内容是0..... 新建模板: 设计文档 当前模板是: 设计文档 内容是: 设计文档的内容是1..... 新建模板: 需求文档 当前模板是: 需求文档 内容是: 需求文档的内容是2..... 当前模板是: 设计文档 内容是: 设计文档的内容是3..... 当前模板是: 设计文档 内容是: 设计文档的内容是4..... 当前模板是: 接口文档 内容是: 接口文档的内容是5..... 新建模板: 简历 当前模板是: 简历 内容是: 简历的内容是6..... 当前模板是: 接口文档 内容是: 接口文档的内容是7..... 当前模板是: 设计文档 内容是: 设计文档的内容是8..... 当前模板是: 设计文档 内容是: 设计文档的内容是9.....
|
UML类图