设计模式之享元模式

设计模式之享元模式

1.享元模式定义:是池技术重要的实现方式。使用共享对象可有效地支持大量细粒度的对象

目的:主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

享元模式为我们提出了两个要求:1.细粒度的对象 2.共享对象

对象的内部状态和外部状态:

内部状态:内部状态指的是对象中可以共享出来的信息。存贮在享元对象对象内部并且不会随对象内部改变而改变的对象。不必直接存储在具体的对象中,属于可以共享的部分

外部状态:外部对象是对象得以依赖的一个标记,是随环境的改变而改变。不可共享的状态,是一批对象的统一标识,是唯一的索引值

2.享元模式通用的类图:

 

类图说明:

FlyWeight:抽象享元角色

简单来说就是一个产品的抽象类,同时定义出对象的外部接口和内部状态的接口和实现

ConcreteFlyweight :具体享元角色

具体的一个角色,实现享元角色定义的业务。该角色需要注意的是内部状态处理与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允许的。

UnsharedConcreteFlyweight: 不可共享的享元角色

FlyweightFactory享元工厂:指责非常简单,是就是构造一个池容器。同时提供从池中获取对象的方法

3.享元模式通用代码

 抽象享元角色:在抽象角色中,一般需要把外部状态和内部状态定义出来,避免子类随意扩展

public abstract class Flyweight {
    // 内部状态
    private String intrinsic;
    // 外部状态 定义为final,防止无意修改了一下,造成池中混乱.在程序中,确认只需要一次赋值的属性,就设置为final.避免无意修改导致逻辑混乱,特别是session级别的常亮或者变量
    protected final String Extrinsic;

    // 要求享元角色必须接受外部状态
    public Flyweight(String extrinsic) {
        Extrinsic = extrinsic;
    }

    // 定义业务操作
    public abstract void operate();
    // 内部状态set/getter

    public String getIntrinsic() {
        return intrinsic;
    }

    public void setIntrinsic(String intrinsic) {
        this.intrinsic = intrinsic;
    }
}

  

 具体享元角色

public class ConcreteFlyWeight extends Flyweight {
    // 接受外部状态
    public ConcreteFlyWeight(String extrinsic) {
        super(extrinsic);
    }

    // 根据外部状态,进行逻辑操作
    @Override
    public void operate() {
        System.out.println("执行操作=========>" + this.Extrinsic);
    }
}

  

享元工厂

public class FlyWeightFactory {

    // 定义一个池对象
    private static HashMap<String, Flyweight> pool = new HashMap<>();

    // 享元工厂
    public static Flyweight getFlyWeight(String extrinsic) {
        // 需要返回的对象
        Flyweight flyweight = null;

        if (pool.containsKey(extrinsic)) {
            flyweight = pool.get(extrinsic);
            System.out.println("直接从池中 取出数据==============>" + extrinsic);
        } else {
            // 根据外部状态撞见享元对象
            flyweight = new ConcreteFlyWeight(extrinsic);
            // 放置到池中
            pool.put(extrinsic, flyweight);
            System.out.println("池子中不存在数据,先创建,在放入池子中=============》" + extrinsic);
        }
        return flyweight;
    }
}

  

测试结果:

public class TestFlyWeight {

    @Test
    public void test001() {
        Flyweight flyweight1 = FlyWeightFactory.getFlyWeight("thread-1");
        flyweight1.operate();
        Flyweight flyweight2 = FlyWeightFactory.getFlyWeight("thread-2");
        flyweight2.operate();
        Flyweight flyweight3 = FlyWeightFactory.getFlyWeight("thread-3");
        flyweight3.operate();
        Flyweight flyweight4 = FlyWeightFactory.getFlyWeight("thread-1");
        flyweight4.operate();
    }
}

运行截图:

外部状态最好用java的基本类型作为标志,如String,int等,可以大幅度提升效率

4.享元模式的优缺点

  优点:大大减少了对象的创建,降低了程序内存的占用,提高程序的性能

  缺点:提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变

5.享元模式的使用场景

  1.系统中存在大量的相似的对象

  2.需要缓冲池的场景

6.注意事项:

  1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。

  2、这些类必须有一个工厂对象加以控制。

7.应用实例:

  1.ResueExecutor对JDBC的statement对象进行了封装。当执行相同SQL语句的时候,直接从缓存中取出Statement对象进行复用,避免了频繁创建和销毁对象

  2.数据库连接池

  3.String.intern方法。如果String的对象池中有该类型的值,则直接返回对象池中的对象,如果没有则创建一个字符串保存在字符串缓存池里面

原文地址:https://www.cnblogs.com/yingxiaocao/p/13514674.html