设计模式(15):享元模式

一、概念

1、定义:减少创建对象的数量,减少内存占用,从而改善应用所需的对象结构的方式(即改善性能)。运用了共享技术有效地支持大量细粒度的对象。(复用对象的思想

2、类型:结构型,英文名FlyWeight(最轻量级)。

3、适用场景

  • 常常应用于系统底层的开发,以便解决系统性能问题。系统中对象较多时,会导致内存溢出,可以把共同的部分抽象出来。
  • 如果系统中有大量的相似对象,需要使用缓冲池的场景。例如一个系统中有大量的细粒度的对象,并且这些对象中有大部分可以外部化。(享元模式的外部化和内部化)。
  • 在有足够多的享元对象可供共享时,才值得我们使用这个模式,享元模式就是复用对象的思想。如果这个对象的复用度很低,那没必要使用这个模式。

4、优缺点

  • 优点
    • 减少对象创建,降低了堆内存中对象的数量,提高系统性能。
    • 减少内存之外的其他资源的占用(减少new关键字生成实例的次数,减少了程序运行时间,提高执行速度)。
  • 缺点
    • 关注内部/外部状态,关注线程安全问题。为了使用享元对象,大部分都是HashMap,是线程不安全的,如果为了线程安全去使用HashTable的话会得不偿失,里面有同步锁。
    • 使系统、程序的逻辑复杂化了,使用享元对象提高了系统的复杂度,要分离出外部状态和内部状态,而且外部状态是不应该随着内部状态的变化而变化的,否则系统就混乱了。

5、扩展

  • 内部状态:可以共享的状态,在享元对象的内部,不会随着外部环境的改变而改变。
  • 外部状态:不可以共享的状态,在享元对象的外部,随着外部环境的改变而改变。

二、Coding

业务场景:网站要求各个部门做年底总结报告,如果这些报告都生成过了就没必要再new一个。

//Employee接口,所有经理都要作报告
public interface Employee {
    //做报告
    void report();
}

//经理类
public class Manager implements Employee {
    private String department;      //管理者所在部门,对于manager实体来说是外部状态,依赖于外部传入的参数
    private String reportContent;   //作报告的内容

    //new管理者需要知道在哪个部门,而报告内容在外部设置就行
    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 {
    //容器,map中key为部门,value为对应的员工
    private static final Map<String, Employee> EMPLOYEE_MAP = new HashMap<>();

    //从EMPLOYEE_MAP容器中获取对应的Manager,入参为department
    public static Employee getManager(String department) {
        Manager manager = (Manager) EMPLOYEE_MAP.get(department);
        //如果没有,就直接new一个,首先系统要创建经理,然后再创建报告,都创建好后放进对象池中,下次如果再让他作报告,直接从对象池中拿
        if (manager == null) {
            manager = new Manager(department);
            System.out.println("创建部门经理:"+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();
        }
    }
}
//测试结果,带空格的都是首次创建
创建部门经理:QA
 创建报告QA部门汇报:此次报告的主要内容是...
QA部门汇报:此次报告的主要内容是...
QA部门汇报:此次报告的主要内容是...
QA部门汇报:此次报告的主要内容是...
创建部门经理:RD
 创建报告RD部门汇报:此次报告的主要内容是...
RD部门汇报:此次报告的主要内容是...
创建部门经理:PM
 创建报告PM部门汇报:此次报告的主要内容是...
PM部门汇报:此次报告的主要内容是...
QA部门汇报:此次报告的主要内容是...
PM部门汇报:此次报告的主要内容是...
RD部门汇报:此次报告的主要内容是...
PM部门汇报:此次报告的主要内容是...
QA部门汇报:此次报告的主要内容是...

案例类图
EmployeeFactoryEmployee是组合关系,EmployeeFactorygetManager返回值是Employee,然后它来创建对应的Manager实例,Manager对应的实现是Employee

关于内部状态与外部状态

public class Manager implements Employee {
    //管理者所在部门,对于manager实体来说是外部状态,依赖于外部传入的参数
    private String department;      
    //如果我们像下面这样写就是内部状态,manager的title是不变的,它不随外部状态(比如department)的变化而变化
  	private String title = "部门经理";
}

假设要用程序画圆形,如果这个半径是固定好的,那就是内部状态,如果半径需要在应用层传入的话,就是外部状态。

三、源码

Ingeger类的valueOf()方法,其中IntegerCatch的范围为-128~127。

public static Integer valueOf(int i) {
    //i的范围在-128~127,这个范围的数java会进行缓存,下一次使用,就会直接从缓存中取出来而不是new Integer对象
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
  	//当这个数字没有进入if里边就要new Integer对象
    return new Integer(i);
}
//测试代码
public class Test {
    public static void main(String[] args) {
        Integer a = Integer.valueOf(100);
        Integer b = 100;
        System.out.println("a == b:" + (a == b));   //true

        Integer c = Integer.valueOf(1000);
        Integer d = 1000;
        System.out.println("c == d:" + (c == d));   //false
    }
}

ab是相等的,且是同一个对象,因为这个100刚好在缓存的范围,cd虽然数字一样,但不是同一个对象,因为d是创建出来的新的Integer对象。同理Long类型也是有LongCatch

发布了43 篇原创文章 · 获赞 6 · 访问量 3907

猜你喜欢

转载自blog.csdn.net/weixin_44424668/article/details/103269710