我们来coding享元模式,享元模式的重点就是共享,网站需要各个管理者上传报告,如果这些报告已经生成过了,
我们就没有必要再去new一个了,我们来通过这场景来体现一下享元模式,这里面我们也会结合工厂模式,首先我们
创建一个包,结构型,这里创建一个享元的包,flyweight,flyweight英文的愿意呢,是指轻量级的,如果用英英字典来解析的话,
最轻量级的类,那在中文翻译成享元模式,还是非常贴切的,那我们首先创建一个员工的接口
首先看这三个类,下面的应用层先不看,这个关系还是非常简单的,重点在这个工厂中,EMPLOYEE_MAP,
然后他两是组合关系,EmployeeFactory的getManager的返回值,是Employee,然后他来创建对应的Manager,
Manager又实现Employee,那整体来说,享元模式理解起来也非常容易,那我们也强调了,在使用享元模式的时候,
我们一定要关注线程安全问题,例如说现在这个Test,那在getManager的时候,这里面使用的就是HashMap,并不是线程
安全的,而且我们这里面也没有加同步锁,那对于作报告这种简单的场景,如果我们多线程过来的时候,其实并没有必要
关注他的线程安全问题,我们说的线程安全问题,其实也是要享元模式一定要保证他的线程安全,这个还是要根据实际的应用
场景进行取舍,那现在我们来思考一下,对于这个部门经理,department这个属性,现在是声明在外部的,Manager的department
属性,它是在外部来声明的,并且Manager的department的状态依赖于外部传入的一个参数,所以我们可以认为他是外部状态,
因为我传入不同的department,所获得的这个Manager,他里面的department的属性是不一样的,对于Manager这个实体,
我们就可以认为这个状态department这个状态,是外部状态,那怎么理解内部状态,很简单,我们写一个private String title,
直接赋值部门经理,这个时候我们外部怎么传department,或者reportContent,Manager的title是不变的,他不随外部的department
的状态变化而变化,他的title永远是部门经理,非常容易理解,之所以把title放到这里面讲,而不是之前讲呢,是因为前面我们也有说,
大家在coding的时候呢,就开始思考,然后在这里面我们再强调一下,并且产生对比,这样你们对外部状态和内部状态肯定会理解的,
那这种例子非常多,假设我们现在要用程序,画圆形,而这个圆形的半径,如果我们是固定好的,那他就是内部状态,如果这个圆形的
半径需要我们在应用层,客户端代码来传入的话,那么他就是外部状态,那我们再思考一个问题,如果这个圆形还要有颜色呢,
希望你们能继续思考一下,非常简单,那享元模式就讲到这里,我们看一下享元模式在一些源码的应用
package com.learn.design.pattern.structural.flyweight;
/**
* 这个接口我们有一个方法report
* 部门的Manager当然也是员工
* 他实现员工这个接口之后呢
* 也就要实现report接口
* 我们再创建一个类
* Manager
*
* @author Leon.Sun
*
*/
public interface Employee {
/**
* 做报告
*
*
*/
void report();
}
package com.learn.design.pattern.structural.flyweight;
/**
* 实现Employee
* 然后实现他的接口
* 既然是部分的Manager
* 他的属性就是所在的部门
*
*
* @author Leon.Sun
*
*/
public class Manager implements Employee {
/**
* 那在report的时候就比较简单了
* 因为报告的内容我已经拿到了
* 所以我们直接输出
* reportContent
* 开始做报告
* 那这两个映射类OK了
* 我们还要创建一个类呢
* 是工厂
* 通过Employee的工厂呢
* 来获取对应的Manager
* 对应的Employee
* 那我们这里只关注Manager
* 因为现在只让Manager做报告
* 我们Employee不需要做报告
*
*
*/
@Override
public void report() {
System.out.println(reportContent);
}
private String title = "部门经理";
/**
* 部门
*/
private String department;
/**
* 那在做报告的时候
* 需要有内容的
* 所以他还持有一个报告的内容
* reportContent
* 这么两个属性
* 那这个Manager在new的时候
* 我肯定是应该知道
* 他在哪个部门的
*
*
*/
private String reportContent;
/**
* 那对于reportContent
* 我们可以在外部重置reportContent
* 所以我们加一个reportContent的set方法
*
*
* @param reportContent
*/
public void setReportContent(String reportContent) {
this.reportContent = reportContent;
}
/**
* 这里加一个构造器
* 这个构造器只传一个department就可以了
*
*
* @param department
*/
public Manager(String department) {
this.department = department;
}
}
package com.learn.design.pattern.structural.flyweight;
import java.util.HashMap;
import java.util.Map;
/**
* EmployeeFactory这个类是工厂类
* 你们肯定是相当熟悉了
* 之前有系统的学习过
* 工厂相关的模式
* 而且在单例模式中
* 也学过容器单例
* 所以这个类如何实现
* 相信大部分是心里有数的
* 那我们这一节就当一个复习
* 非常简单
* 轻松加愉快的来学习享元模式
*
*
*
* @author Leon.Sun
*
*/
public class EmployeeFactory {
/**
* 首先我们来创建一个容器
* final的Map
* key是String
* value是Employee
* 这个就叫EMPLOYEE_MAP
* 因为我们使用static和final
* IDEA会自动提示使用大写的命名
* 直接new一个HashMap
* 然后泛型写上
* 我们都是用原生的JDK来做
* 现在把Mape的包导入一下
*
*
*/
private static final Map EMPLOYEE_MAP = new HashMap();
/**
* 我们再写一个方法
* 返回者肯定是这个类getManager
* 就是需要从EMPLOYEE_MAP中获取对应的Manager
* Manager也是Employee
* 接下来想要哪个经理来做报告
* 这个部门我们就要交给外部来传
* 以后我们在使用的时候
* 你们就要回想一下
* 我们之前有讲过内部状态和外部状态
* 这个时候大家就可以跟着思考一下
* 这个时候我们传入一个入参
* department
*
*
*
* @param department
* @return
*/
public static Employee getManager(String department){
/**
* 然后获取一个Manager
* 直接从这里面获得一个Manager
* 赋值成什么呢
* 肯定是从Map里面直接取
* get的key肯定就是部门
* 这里面需要我们强转
*
* 首先从Map里面获取一个Manager
*
*
*/
Manager manager = (Manager) EMPLOYEE_MAP.get(department);
/**
* 这个时候做一些空判断
* 如果manager是空的话
*
* 如果没有获取到
* 就new一个放进去
* 如果我们还想让同一部门的经理来做报告的话
* 那我们直接从对象池中取出来
* 就不需要new了
* 接下来我们来写一下主函数
* Test测试类
*
*
*/
if(manager == null){
/**
* 那我们就new一个manager出来
* 把department传进去
*
* manager现在有部门了
* 那还需要让他来创建报告
*
*
*/
manager = new Manager(department);
System.out.print("创建部门经理:"+department);
/**
* 然后把报告内容统一一下
*
*
*/
String reportContent = department+"部门汇报:此次报告的主要内容是......";
/**
* 所以我们直接在这里面manager.setReportContent
* 首先是部门名称加上部门汇报
* 谁来汇报
* 此处的报告内容是
* 开始做报告了
* 那我们把这个顺序改一下
* 设置完报告呢
* 就输出一个创建报告
* 然后reportContent放到这里边
* 这样当这个经理第一次来做报告的时候
* 首先我们这个系统要创建这个经理
* 然后再创建这个报告
* 都创建好之后
* 放到这个对象池里边
* 下次做报告直接从池里拿
* 不再走这个new的过程
* 我们现在回到Test里边
*
*
*/
manager.setReportContent(reportContent);
System.out.println(" 创建报告:"+reportContent);
/**
* 然后往EMPLOYEE_MAP中put
* 把这个department和manager放进去
*
*
*/
EMPLOYEE_MAP.put(department,manager);
}
/**
* 直接return manager
* 非常简单
* 那捋一下逻辑
*
*
*/
return manager;
}
}
package com.learn.design.pattern.structural.flyweight;
/**
* 也就是应用层的代码
*
*
* @author Leon.Sun
*
*/
public class Test {
/**
* 首先我们声明一个部门的常量
* final的
* 我们直接用一个String的数组就行
* departments的一个数组
* 这里面我们想象一下
* 我们有研发部门
* 还有呢QA部门
* 还有PM部门
* 还有BD部门
* 其他的部门我们就不写了
*
*
*/
private static final String departments[] = {"RD","QA","PM","BD"};
/**
* 我们写一个主函数
*
*
* @param args
*/
public static void main(String[] args) {
/**
* 从这个部门随机的取
* 有一个for循环
*
*
*/
for(int i=0; i<10; i++){
/**
* 首先我们有一个department
* 这里面有一个随机数从departments数组里随机的取
* 也就是说用这个数组的索引取随机数
* 这样这个部门也就随机了
* 打个比方
* 今天副总经理让研发的Manager做报告
* 过了几天呢
* 总经理又来说
* 研发的经理过来
* 大概年底的时间
* 部门的Manager经常要做报告
* 各种报告层出不穷
* 所以这里还真的特别适合响应模式
* 我们接着写
* 从departments里面直接取一个
* 取一个下角标
* Math.random()这个方法
* 让他乘上departments的length
* 就OK了
* 进行类型的转换
* 把它转换成int
* 这样就从里面随机的获取元素
* 来获取这个部门的名称
* 然后Manager
*
*
*
*/
String department = departments[(int)(Math.random() * departments.length)];
/**
* 来获取这个Manager
*
* EmployeeFactory.getManager
* 把department传进去
* 这里面按照提示强转一下
* 那在Manager第一次做报告的时候
* 报告还没有准备好
* 所以第一次的时候
* 他需要new一个报告出来
* 那以后就不需要new了
* 就直接拿过来
* 开始做汇报
* 所以我们来到getManager这里边
*
*
*
*/
Manager manager = (Manager) EmployeeFactory.getManager(department);
/**
* 做报告
*
* manager点report方法
* 那么现在来run一下
* 来看一下结果是什么样子的
* 我们看一下
* 这里是10次的循环
* 首先把QA的经理叫过来了
* 让他做报告
* 他赶快创建一个报告
* 主要是......
* 而第二次的时候
* 又让他做一次报告
* 他呢不需要再创建了
* 我这里有现成的
* 对于我们这个对象池我们这个经理也在
* 他的报告也在
* 那第三次叫BD的经理来做报告了
* 然后连着让他做两次
* 又叫QA的部门来做报告了
* 他还是直接拿过来做报告
* 然后叫PM的部门来做报告
* 然后叫RD的部门来做报告
* 那这些都是一样的
* 创建过程也是一样的
* 我们接着回来
* 因为这里的代码业务逻辑非常简单
* 就不领着一起来debug了
* 而且我们在之前学习容器单例的时候
* 这个知识点都有学过
* 而且这里面的逻辑清晰而简单
* 那我们来解析一下他的类图
*
*
*
*/
manager.report();
}
// Integer a = Integer.valueOf(100);
// Integer b = 100;
//
// Integer c = Integer.valueOf(1000);
// Integer d = 1000;
//
// System.out.println("a==b:"+(a==b));
//
// System.out.println("c==d:"+(c==d));
}
}