设计模式十一之享元模式
在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈。这些对象有很多相似的地方,如果能把它们相同的部分提取出来共享,则能节省大量的系统资源,这就是享元模式的产生背景。
1. 模式的定义与特点
1.1 模式的定义
享元模式(Flyweight):运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
1.2 模式的特点
享元模式的优点有:
1. 相同对象主要保存一份,降低系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力;
2. 减少内存之外的其他资源的占用
享元模式的缺点有:
1. 关注内、外部状态,关注线程安全问题;
2. 使系统、程序的逻辑复杂化。
1.3 模式的使用场景
1. 常常应用于系统底层的开发,以便解决系统的性能问题;
2. 系统有大量相似对象、需要缓冲池的场景。
2. 模式的结构与实现
享元模式中存在以下两种状态:
1. 内部状态:不会随着环境的改变而改变的可共享部分;
2. 外部状态:随着环境改变而改变的不可共享的部分,享元模式实现的要领就是要区分这两种状态,并将外部状态外部化。
2.1 模式的结构
享元模式的主要角色如下:
1. 抽象享元角色(Flyweight):是所有具体享元类的基类,为具体享元类规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法注入;
2. 具体享元角色(Concrete Flyweight):实现抽象享元角色中定义的接口;
3. 是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中;
4. 享元工厂角色(Flyweight Factory):负责创建和管理享元角色,当客户请求一个享元角色时,享元工厂检查系统中是否存在符合条件的享元对象,如果存在则返回给客户,如果不存在,则创建一个新的享元对象。
2.2 模式的实现
抽象享元
/**
* 抽象享元 - 员工类
*/
public interface Employee {
/**
* 作报告
*/
void report();
}
具体享元*
/**
* 具体享元 - 经理类
*/
public class Manager implements Employee {
private ReportInfo info;
public Manager(ReportInfo info) {
this.info = info;
}
@Override
public void report() {
System.out.println("部门:" + info.getDepartment() + ", 报告内容为:"
+ info.getReportContent());
}
}
非享元角色
/**
* 非享元角色 - 报告内容
*/
public class ReportInfo {
private String department;
private String reportContent;
public ReportInfo() {
}
public ReportInfo(String department, String reportContent) {
this.department = department;
this.reportContent = reportContent;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getReportContent() {
return reportContent;
}
public void setReportContent(String reportContent) {
this.reportContent = reportContent;
}
}
享元工厂
/**
* 享元工厂
*/
public class EmployeeFactory {
private static final Map<String, Employee> EMPLOYEE_MAP = new HashMap<>();
public static Employee getManger(ReportInfo info) {
Manager manager = (Manager) EMPLOYEE_MAP.get(info.getDepartment());
if (manager == null) {
manager = new Manager(info);
System.out.println("创建了部门经理:" + info.getDepartment());
EMPLOYEE_MAP.put(info.getDepartment(), manager);
}
return manager;
}
}
客户端
public class Client {
public static void main(String[] args) {
String[] departments = {"RD", "QA", "DB", "OP"};
Stream.iterate(0, i -> i + 1).limit(10).forEach(i -> {
String department = departments[(int) (Math.random() * departments.length)];
String content = "conent_" + i + "...";
Manager manger = (Manager) EmployeeFactory.getManger(new ReportInfo(department, content));
manger.report();
});
}
}
# 运行结果如下:
创建了部门经理:OP
部门:OP, 报告内容为:conent_0...
部门:OP, 报告内容为:conent_0...
创建了部门经理:DB
部门:DB, 报告内容为:conent_2...
创建了部门经理:RD
部门:RD, 报告内容为:conent_3...
部门:RD, 报告内容为:conent_3...
创建了部门经理:QA
部门:QA, 报告内容为:conent_5...
部门:QA, 报告内容为:conent_5...
部门:DB, 报告内容为:conent_2...
部门:QA, 报告内容为:conent_5...
部门:RD, 报告内容为:conent_3...
3. 模式在开源软件中的应用
3.1 java.lang.Integer 类
public final class Integer extends Number implements Comparable<Integer> {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
}
如果在 -128 - 127 之间就直接从缓存中获取,否则就创建新的对象。