享元模式(Flyweight)---结构型

1 基础知识

定义:提供了减少对象数量从而改善应用所需的对象结构的方式。特征:运用共享技术有效支持大量细粒度的对象。

本质:分离与共享。

使用场景:

(1)如果一个应用程序使用了大量的细粒度对象,可以使用享元模式来减少对象数量。如果由于使用大量的对象,造成很大的存储开销,可以使用享元模式来减少对象数量,并节约内存。

(2)如果对象的大多数状态都可以转变为外部状态,比如通过计算得到,或是从外部传入等,可以使用享元模式来实现内部状态和外部状态的分离。

(3)如果不考虑对象的外部状态,可以用相对较少的共享对象取代很多组合对象,可以使用享元模式来共享对象,然后组合对象来使用这些共享对象。

优点:大大减少对象的创建,降低系统的内存,使效率提高;减少内存之外其他资源占用。缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱;可能有线程安全问题;维护共享对象,需要额外开销。

注:并不是所有的对象都适合存,因为存的是对象的实例,实例里面存放的主要是对象属性的值。因此,如果被缓存的对象的属性值经常变动那就不适合缓存了,因为真实对象的属性值变化了,那么缓存中的对象也必须要跟着变化,否则缓存中的数据就跟真实对象的数据不同步,可以说是错误的数据了。

    因此,需要分离出被缓存对象实例中,哪些数据是不变且重复出现的,哪些数据是经常变化的,真正应该被缓存的数据是那些不变且重复出现的数据,把它们称为对象的内部状态,而那些变化的数据就不缦存了,把它们称为对象的外部状态。这样在实现的时候,把内部状态分离出来共享,称之为享元,通过共享享元对象来减少对内存的占用。把外部状态分离出来,放到外部,让应用在使用的时候进行维护,并在需要的时候传递给享元对象使用。为了控制对内部状态的共享,并且让外部能简单地使用共享数据,提供一个工厂来管理享元,把它称为享元工厂

 2 代码示例

场景:假设公司年底要做报告,需要经理多次随机的报告。

员工接口:

public interface Employee {
    void report();
}

 经理类 Manager :实现员工接口

public class Manager implements Employee {
    @Override
    public void report() {
        System.out.println(reportContent);
    }
    private String title = "部门经理";
    //部门
    private String department;
    //报告内容
    private String reportContent;
    
    //set方式注入对象
    public void setReportContent(String reportContent) {
        this.reportContent = reportContent;
    }

    public Manager(String department) {
        this.department = department;
    }   
}

员工工厂:EmployeeFactory 创建部门经理和报告内容

public class EmployeeFactory {
private static final Map<String,Employee> EMPLOYEE_MAP = new HashMap<String,Employee>(); 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();
        }
    }
}

 外部状态:在经理类中的department属性便是外部状态,是会随机发生变化的。内部状态:经理类中的title属性便是内部状态,无论怎么变都不会发生变化,这里是title在程序中并没有应用到只是作为分析的。

 3 相关模式

(1)享元模式与单例模式

 这两个模式可以组合使用。通常情况下,享元模式中的享元工厂可以实现成为单例。另外,享元工厂中缓存的享元对象,都是单实例的,可以看成是单例模式的一种变形控制,在享元工厂中来单例享元对象。

(2)享元模式与组合模式

这两个模式可以组合使用。在享元模式中,存在不需要共享的享元实现,这些不需要共享的享元通常是对共享的享元对象的组合对象。也就是说,享元模式通常会和组合模式组合使用,来实现更复杂的对象层次结构。

(3)享元模式与状态模式

这两个模式可以组合使用。可以使用享元模式来共享状态模式中的状态对象。通常在状态模式中,会存在数量很大的、细粒度的状态对象,而且它们基本上都是可以重复使用的,都是用来处理某一个固定的状态的,它们需要的数据通常都是由上下文传入,也就是变化部分都分离出去了,所以可以用享元模式来实现这些状态对象。

(4)享元模式与策略模式

这两个模式可以组合使用。可以使用享元模式来实现策略模式中的策略对象。和状态模式一样,在策略模式中也存在大量细粒度的策略对象,它们需要的数据同样是从上下文传入的,所以可以使用享元模式来实现这些策略对象。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0

posted @ 2019-08-14 10:57  windy杨树  阅读(246)  评论(0编辑  收藏  举报