该项目源码地址:https://github.com/lastwhispers/code/tree/master/java-basic/design-pattern
(设计模式相关代码与笔记)
1. 定义
提供了减少对象数且从而改善应用所需的对象结构的方式。运用共享技术有效地支持大是细粒度的对象。
2. 适用场景
- 常常应用于系统底层的开发,以便解决系统的性能问题。
- 系统有大量相似对象、需要缓冲池的场景。
比如:String常量池、Integer常量池、数据库连接池、线程池
3. 类图与角色
享元模式类图享元模式存在如下几个角色:
Flyweight:抽象享元类。所有具体享元类的超类或者接口,通过这个接口,Flyweight可以接受并作用于外部专题 。
ConcreteFlyweight:具体享元类。指定内部状态,为内部状态增加存储空间。
UnsharedConcreteFlyweight:非共享具体享元类。指出那些不需要共享的Flyweight子类。
FlyweightFactory:享元工厂类。用来创建并管理Flyweight对象,它主要用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory就会提供一个已经创建的Flyweight对象或者新建一个(如果不存在)。
享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
4. 内部状态与外部状态
内部状态:在享元对象内部不随外界环境改变而改变的共享部分
外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态
5. 相关设计模式
享元模式和代理模式
- 代理模式生成的代理类,可以使用享元模式来生成代理类。
享元模式和单例模式
- 享元模式可以再次创建对象,也可以取缓存对象。单例模式则是严格控制只有一个实例对象。
- 享元模式可以通过自己实现对外部的单例,也可以在需要的使用创建更多的对象。单例模式是自身控制,需要增加不属于该对象本身的逻辑。
6. 模式实例
背景:公司要求各个部门的管理者做一下年底总结报告,如果这些报告都已经生成过了,那么我们就没有必要再去new一个了;我们通过这么一个场景来体现一下享元模式:
6.1 模式基本结构
抽象享元类 有一个员工接口:
public interface Employee {
void report();
}
具体享元类 还有一个经理类:
public class Manager implements Employee {
private String department;
private String reportContent;
public Manager(String department) {
this.department = department;
}
/** 我们可以在外部来重置年终报告 */
public void setReportContent(String reportContent) {
this.reportContent = reportContent;
}
@Override
public void report() {
System.out.println(reportContent);
}
}
享元工厂类 还有一个经理工厂类:
public class EmployeeFactory {
/**
* 定义一个池容器
*/
private static final Map<String, Employee> EMPLOYEE_MAP = new HashMap<>();
/**
* 享元工厂
*
* @param department
* @return
*/
public static Employee getManager(String department) {
Manager manager = (Manager) EMPLOYEE_MAP.get(department);
if (manager == null) {
manager = new Manager(department);
System.out.print("创建部门经理" + department);
String reportContent = department + "部门汇报:此次报告的主要内容是......";
manager.setReportContent(reportContent);
System.out.println(" 创建报告" + reportContent);
//享元工厂
EMPLOYEE_MAP.put(department, manager);
}
return manager;
}
}
测试:
public class Test {
private static final String departments[] = {"RD","QA","PM","BD"};
public static void main(String[]args){
for (int i = 0; i < 10; i++) {
String department = departments[(int) (Math.random() * departments.length)];
Manager manager = (Manager) EmployeeFactory.getManager(department);
manager.report();
}
}
}
测试结果:
测试结果此时类图:
类图6.2 内部状态与外部状态
内部状态:在享元对象内部不随外界环境改变而改变的共享部分
外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态
对于Manager雷,成员变量department就是外部状态,随着Manager的变化而变化。title是内部状态,Manager怎么变,title也不会变化。
public class Manager implements Employee {
/**
* 内部状态
*/
private String title = "部门经理";
/**
* 外部状态
*/
private String department;
private String reportContent;
public Manager(String department) {
this.department = department;
}
/**
* 我们可以在外部来重置年终报告
*/
public void setReportContent(String reportContent) {
this.reportContent = reportContent;
}
@Override
public void report() {
System.out.println(reportContent);
}
}
7. 优缺点
优点:
- 减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率
- 减少内存之外的其他资源占用
缺点:
- 享元的对象无法拥有独立的行为
- 关注内/外部状态、关注线程安全问题
- 使系统、程序的逻辑复杂化
8. 模式总结
1、享元模式可以极大地减少系统中对象的数量。但是它可能会引起系统的逻辑更加复杂化。
2、享元模式的核心在于享元工厂,它主要用来确保合理地共享享元对象。
3、内部状态为不变共享部分,存储于享元享元对象内部,而外部状态是可变部分,它应由客户端来负责。
9. 扩展-JDK1.7以及框架源码中的享元模式
9.1 Integer类里面的valueOf方法与IntegerCache;
valueOf方法与IntegerCache分析
9.2 tomcat的连接池:GenericObjectPoolConfig
org.apache.commons.pool2.impl.GenericKeyedObjectPool
网友评论