Java 设计模式之享元学习与掌握

Posted 邹奇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 设计模式之享元学习与掌握相关的知识,希望对你有一定的参考价值。

定义

  • 运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

优点

  • 相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

缺点

  • 读取享元模式的外部状态会使得运行时间稍微变长。
  • 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。

模式结构与实现

享元模式的定义提出了两个要求,细粒度共享对象

因为要求细粒度,所以不可避免地会使对象数量多且性质相近。而这些对象又可以分为两个部分:内部状态和外部状态。

  • 内部状态指对象共享出来的信息,存储在享元信息内部,并且不会随环境的改变而改变;
  • 外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。

比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。

享元模式的本质是缓存共享对象,降低内存消耗。


模式结构

主要角色如下:

  • 【抽象享元角色】是一个接口,定义方法让具体享元实现;非享元的外部状态以参数的形式传入方法
  • 【具体享元角色】实现抽象享元角色中所规定的接口。
  • 【非享元角色】是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
  • 【享元工厂角色】负责创建和管理享元角色。使用map管理,map中存在则取出来,map中不存在则新建并保存到map。

同学们如果概念看起来脑阔疼,就不用细看,直接看下面实现的代码即可,自己理解就好,不必非得浪费时间去细扣概念,毕竟实践才是最终目的。


模式的实现

【非享元角色】,代码如下:

package com.example.designmodedemo.principle.flyweight;

/**
 * 非享元角色
 */
public class UnsharedFlyWeightObj {

    private String info;

    // 构造
    public UnsharedFlyWeightObj(String info) {
        this.info = info;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

【抽象享元角色】,代码如下:

package com.example.designmodedemo.principle.flyweight;

/**
 * 抽象享元角色
 * - 定义接口方法,非享元对象的外部状态以参数的形式传入
 */
public interface IFlyWeight {

    void execute(UnsharedFlyWeightObj unsharedObj);

}

【具体享元角色】,代码如下:

package com.example.designmodedemo.principle.flyweight;

/**
 * 具体享元角色
 * - 实现抽象享元接口
 */
public class ConcreteFlyWeight implements IFlyWeight{

    private String key;

    public ConcreteFlyWeight(String key) {
        this.key = key;
        System.out.println(":> 具体享元 " + key + " 被创建");
    }

    @Override
    public void execute(UnsharedFlyWeightObj unsharedObj) {
        System.out.println(":> 非享元信息是 - " + unsharedObj.getInfo());
    }
}

【享元工厂角色】,代码如下:

package com.example.designmodedemo.principle.flyweight;

import java.util.HashMap;

/**
 * 享元工厂角色
 * - 根据 key 来管理具体享元;存在则从map中获取,不存在则新建具体享元并存入map中
 */
public class FactoryFlyWeight {

    private HashMap<String, IFlyWeight> flyWeightMap = new HashMap<>();

    public IFlyWeight getFlyWeight(String key){

        IFlyWeight flyWeight = flyWeightMap.get(key);
        if (flyWeight != null){
            System.out.println(":> 具体享元 " + key + " 已经存在,并从缓存中获取");
        }else {
            flyWeight = new ConcreteFlyWeight(key);
            flyWeightMap.put(key, flyWeight);
        }

        return flyWeight;
    }

}

【客户端调用】,代码如下:

	/**
     * 享元模式
     */
    private void principle_flyweight() {
        FactoryFlyWeight factory = new FactoryFlyWeight();
        IFlyWeight fly1 = factory.getFlyWeight("fly1");
        fly1.execute(new UnsharedFlyWeightObj("第一次执行"));
        IFlyWeight fly2 = factory.getFlyWeight("fly1");
        fly2.execute(new UnsharedFlyWeightObj("第二次执行"));
    }

运行结果,如下图示:


应用场景

当系统中多处需要同一组信息时,可以把这些信息封装到一个对象中,然后对该对象进行缓存,这样,一个对象就可以提供给多出需要使用的地方,避免大量同一对象的多次创建,降低大量内存空间的消耗。

享元模式其实是工厂方法模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能。

享元模式是通过减少内存中对象的数量来节省内存空间的,所以以下几种情形适合采用享元模式:

  • 系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
  • 大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
  • 由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。

技术永不眠!我们下期见!

以上是关于Java 设计模式之享元学习与掌握的主要内容,如果未能解决你的问题,请参考以下文章

学习设计模式之享元模式

JAVA设计模式之享元模式(flyweight)

JAVA设计模式之享元模式(flyweight)

入门设计模式之享元

设计模式 --面试高频之享元模式

Java设计模式之享元模式