java设计模式-享元模式

来源:互联网 发布:软件研发部 职能 编辑:程序博客网 时间:2024/06/10 19:19

享元模式

享元模式是设计模式中少数几个以提高系统性能为目的的模式之一。它的核心思想是:如果在一个系统中存在多个相同的对象,那么只需共享一份对象的拷贝,而不必为每一次使用都创建新的对象。在享元模式中,由于需要构造和维护这些可以共享的对象,因此,常常会出现一个工厂类,用于维护和创建对象。

享元模式对性能提升的主要帮助有两点:

(1)可以节省重复创建对象的开销,因为被享元模式维护的相同对象只会被创建一次,当创建对象比较耗时时,便可以节省大量时间。

(2)由于创建对象的数量减少,所以对系统内存的需求也减小,这将使得GC的压力也相应地降低,进而使得系统拥有一个更健康的内存结构和更快的反应速度。

享元模式的主要角色由享元工厂、抽象享元、具体享元类和主函数几部分组成。它们的功能如表2.3所示。

表2.3  享元模式角色

角    色

作    用

享元工厂

用以创建具体享元类,维护相同的享元对象。

它保证相同的享元对象可以被系统共享。即,

其内部使用了类似单例模式的算法,当请求对象

已经存在时,直接返回对象,不存在时,再创建对象

抽象享元

定义需共享的对象的业务接口。享元类被创建

出来总是为了实现某些特定的业务逻辑,

而抽象享元便定义这些逻辑的语义行为

具体享元类

实现抽象享元类的接口,完成某一具体逻辑

Main

使用享元模式的组件,通过享元工厂取得享元对象

基于以上角色,享元模式的结构如图2.5所示。

 图2.5  享元模式类图

享元工厂是享元模式的核心,它需要确保系统可以共享相同的对象。一般情况下,享元工厂会维护一个对象列表,当任何组件尝试获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类;若没有,则创建一个新的享元对象,并将它加入到维护队列中。

注意:享元模式是为数不多的、只为提升系统性能而生的设计模式。它的主要作用就是复用大对象(重量级对象),以节省内存空间和对象创建时间。

享元模式的一个典型应用是在SAAS系统中。SAAS即Software As A Service,是目前比较流行的一种软件应用模式。

以一个人事管理系统的SAAS软件为例,假设公司甲、乙、丙均为这个SAAS系统的用户,则定义每个公司为这套系统的一个租户。每个公司(租户)又各有100个员工。如果这些公司的所有员工都可以登录这套系统查看自己的收入情况,并且为了系统安全,每个公司(租户)都拥有自己独立的数据库。为了使系统的设计最为合理,在这种情况下,便可以使用享元模式为每个租户分别提供工资查询的接口,而一个公司(租户)下的所有员工可以共享一个查询(因为一个租户下所有的员工数据都存放在一个数据库中,它们共享数据库连接)。这样,系统只需要3个享元实例,就足以应付300个员工的查询请求了。系统的结构如图2.6所示。

 图2.6  享元模式的示例

图2.6中,ReportManagerFactory为享元工厂,负责创建具体的报表工具,它确保每个公司(租户)下所有的员工,都共享一个具体的享元实例(FinancialReportManager或者EmployeeReportManager)。这样,当公司甲的两个员工登录,进行财务查询时,系统不必为两个员工都新建FinancialReportManager,而可以让他们共享一个FinancialReportManager实例。

通过这个示例,还可以进一步了解享元工厂和对象池的一个重要区别。在一个对象池中,所有的对象都是等价的,任意两个对象在任何使用场景中都可以被对象池中的其他对象代替。而在享元模式中,享元工厂所维护的所有对象都是不同的,任何两个对象间不能相互代替。如本例中,为公司甲创建的FinancialReportManagerA和为公司乙创建的FinancialReportManagerB分别对应了后台各自不同的数据库,因此,两者是不可相互替代的。

注意:享元模式和对象池的最大不同在于:享元对象是不可相互替代的,它们各自都有各自的含义和用途;而对象池中的对象都是等价的,如数据库连接池中的数据库连接。

本例中享元对象接口的实现如下,它用于创建一个报表。即,所有的报表生成类将作为享元对象在一个公司(租户)中共享。

  1. public interface IReportManager {  
  2.     public String createReport();  
  3. }  
以下是两个报表生成的实例,分别对应员工财务收入报表和员工个人信息报表。它们都是具体的享元类。
  1. public class FinancialReportManager implements IReportManager {//财务报表  
  2.     protected String tenantId=null;  
  3.     public FinancialReportManager(String tenantId){          //租户ID  
  4.         this.tenantId=tenantId;  
  5.     }  
  6.     @Override  
  7.     public String createReport() {  
  8.         return "This is a financial report";  
  9.     }  
  10. }  
  11.  
  12. public class EmployeeReportManager implements IReportManager { //员工报表  
  13.     protected String tenantId=null;  
  14.     public EmployeeReportManager(String tenantId){               //租户ID  
  15.         this.tenantId=tenantId;  
  16.     }  
  17.     @Override  
  18.     public String createReport() {  
  19.         return "This is a employee report";  
  20.     }  
  21. }  
最为核心的享元工厂类实现如下,它也是享元模式的精髓所在。它确保同一个公司(租户)使用相同的对象产生报表。这是相当有意义的,否则系统可能会为每个员工生成各自的报表对象,导致系统开销激增。
  1. public class ReportManagerFactory {  
  2.       
  3.     Map<String ,IReportManager> financialReportManager=  
  4. new HashMap<String ,IReportManager>();  
  5.     Map<String ,IReportManager> employeeReportManager=  
  6. new HashMap<String ,IReportManager>();  
  7.       
  8.     IReportManager getFinancialReportManager(String tenantId){  
  9.                                                             //通过租户ID  
  10.                                                             //获取享元  
  11.         IReportManager r=financialReportManager.get(tenantId);  
  12.         if(r==null){  
  13.             r=new FinancialReportManager(tenantId);  
  14.             financialReportManager.put(tenantId, r);        //维护已创建的  
  15.                                                             //享元对象  
  16.         }  
  17.         return r;  
  18.     }  
  19.       
  20.     IReportManager getEmployeeReportReportManager(String tenantId){  
  21.                                                             //通过租户ID  
  22.                                                             //获取享元  
  23.         IReportManager r=employeeReportManager.get(tenantId);  
  24.         if(r==null){  
  25.             r=new EmployeeReportManager(tenantId);  
  26.             employeeReportManager.put(tenantId, r);         //维护已创建的  
  27.                                                             //享元对象  
  28.         }  
  29.         return r;  
  30.     }  
  31. }  
使用享元模式的方法如下:
  1. public static void main(String[] args) {  
  2.     ReportManagerFactory rmf=new ReportManagerFactory();  
  3.     IReportManager rm=rmf.getFinancialReportManager("A");  
  4.     System.out.println(rm.createReport());  
  5. }  
ReportManagerFactory作为享元工厂,以租客的ID为索引,维护了一个享元对象的集合,它确保相同租客的请求都返回同一个享元实例,确保享元对象的有效复用。
0 0