一、什么是享元模式?
① 存在的意义:
如果一个系统中存在着很多个业务都会去调用同一类的业务实例,那么每次调用都会创建实例,显而易见,这对GC的压力是非常大的。
② 实现过程:
通过对同一类业务实例进行封装,使用“享元工厂”对“抽象享元”的一个单例操作,在享元工厂中通过一个 id 来标识要操作的同一类业务实例,通过HashMap存储不同类别的实例。
(此处解释可能不太好理解,需要先看懂下面的代码,当然看懂也不一定懂,看完把代码敲一遍,更好理解)
二、具体代码demo
首先得知道享元模式四大角色:抽象享元、具体享元类、享元工厂、Main使用
①抽象享元:
也就是定义这些业务实例(享元类)的共同行为,对其进行封装,实现多态。
/**
* 享元对象接口实现(抽象享元)
*
* 对业务实现进行封装
*/
public interface IReportManager {
//共同的行为(业务)
String createReport();
}
②具体享元类:
也就是不同业务(享元类)的实现,它们都实现了抽象享元类
package flyweight;
/**
* @Author : WJ
* @Date : 2019/4/27/027 9:03
* <p>
* 注释: 具体的享元类 FinacialReportManager 、EmployeeReportManger (也就是具体业务的核心实现)
*/
public class FinacialReportManager implements IReportManager{
//tendId标识同一类业务实例
protected String tendId = null;
FinacialReportManager(String tendId){
this.tendId = tendId;
}
@Override
public String createReport() {
return "this is Finacial ReportManager" + tendId;
}
}
//--------------------------------------------------------------------//
class EmployeeReportManger implements IReportManager{
protected String tendId = null;
EmployeeReportManger(String tendId){
this.tendId = tendId;
}
@Override
public String createReport() {
return "this is Employee ReportManger" + tendId;
}
}
③享元工厂:
最核心的享元模式角色,内部封装两个方法,让外部可以通过这个工厂调用这些方法获取相应业务实例,同时这些方法内部又采用的类似“单例模式”的设计风格,实现同一类业务的实例获取是单例效果的。同时,不同的业务实例创建与存储,都有不同的HashMap对应。
package flyweight;
import java.util.HashMap;
import java.util.Map;
/**
* @Author : WJ
* @Date : 2019/4/27/027 9:09
* <p>
* 注释: 最核心的享元工厂类
*/
public class ReportMangerFactory {
//hashmap保存创建的实例对象,用tendId表示不同类别的用户对象
Map<String , IReportManager> financialReportManger = new HashMap();
Map<String , IReportManager> employeeReportManger = new HashMap();
//根据tendId获取实例,如果是同一类事务要使用同一类实例,那么在第一次创建实例后,就可以延用此实例
//减少创建实例的开销
IReportManager getFinacialReportManger(String tendId){
IReportManager r = financialReportManger.get(tendId);
if(r == null){
r = new FinacialReportManager(tendId);
System.out.println("新创建实例成功!tendId为:"+tendId);
financialReportManger.put(tendId , r);
}else{
System.out.println("您已经创建过了!同一类事务享用创建好的实例对象"+tendId);
}
return r;
}
IReportManager getEmployeeReportManger(String tendId){
IReportManager r = employeeReportManger.get(tendId);
if(r == null){
r = new EmployeeReportManger(tendId);
System.out.println("新创建实例成功!tendId为:"+tendId);
employeeReportManger.put(tendId , r);
}else{
System.out.println("您已经创建过了!同一类事务享用创建好的实例对象"+tendId);
}
return r;
}
}
④最后-使用:
package flyweight;
/**
* @Author : WJ
* @Date : 2019/4/27/027 9:14
* <p>
* 注释: 使用享元模式
*
*/
public class Main {
public static void main(String[] a){
ReportMangerFactory rmf = new ReportMangerFactory();
//多次获取同一业务实例A
IReportManager rm1 = rmf.getFinacialReportManger("A");
IReportManager rm2 = rmf.getFinacialReportManger("A");
IReportManager rm3 = rmf.getFinacialReportManger("A");
//多次获取同一业务实例B
IReportManager rm4 = rmf.getEmployeeReportManger("B");
IReportManager rm5 = rmf.getEmployeeReportManger("B");
IReportManager rm6 = rmf.getEmployeeReportManger("B");
}
}
最后运行结果:
image.png
思考
①在什么场景下使用享元模式?
------根据上面对享元模式的理解,也即是某一和系统如果有不同模块的业务,每个模块下又有很多小模块,这些小模块需要同一个实例,那么采用享元模式能够大大减少实例对象创建,造成频繁GC。
(一个典型的应用就是“SAAS”系统,这是一个软件应用模式,就以一个人事管理系统的“SAAS为例”,甲乙丙三个公司共同管理这个系统上的某一些人,但是甲乙丙各自内部又有成百上千个员工,各自公司都有一套业务流程去管理这个系统的那些人,各个公司内部的每个人如果每次登陆都需要重新做一些重复相同的流程的话,这就会非常的没有效率,享元模式就是为了减少这些重复操作相同操作,以节省内存空间和对象创建时间)
网友评论