设计模式 - 享元模式(池化技术)

享元模式:①将对象的公共部分抽取出来成为内部状态(实现共享),②将随时间改变、不可共享的部分作为外部状态(通过更换外部状态实现对象复用),从而减少创建对象的数量,以减少内存开销和提高性能。
核心:共享和复用,共享(内部状态 - intrinsicState),复用(外部状态 - extrinsicState)
角色:

  • FlyweightFactory(享元工厂):多和单例模式一起使用,维护一个享元池
  • Flyweight(享元对象):作为共享和复用的对象,其存在内部状态和外部状态

使用场景:

  • 共享:当只存在内部状态时,可以在多线程中共享使用(String常量池)
  • 复用:通过改变外部状态,可以更好实现对象的复用(线程池)
    PS:外部状态在线程间需考虑并发问题,因此不适合共享,但当对象被使用完成后,通过修改外部状态,使其可以复用于下一次的访问需求

Flyweight示例

// 享元工厂,维护一个享元池,以及添加和获取享元对象
public class FlyweightFactory {
    private static Map<String, IFlyweight> pool = new HashMap<>();

    public static IFlyweight getFlyweight(String key){
        if(pool.containsKey(key)){
            return pool.get(key);
        } else {
            IFlyweight result = new ConcreteFlyweight(key);
            pool.put(key, result);
            return result;
        }
    }
}
public interface IFlyweight {
    void operation();
    void setExtrinsicState(String extrinsicState);
}

public class ConcreteFlyweight implements IFlyweight{
    // 内部状态,对象创建之后即不再改变,特性:共享
    private String intrinsicState;

    // 外部状态,在运行过程中可以被改变的状态(与请求相关的变量),功能:复用
    private String extrinsicState;

    // 内部状态由构造方法进行设置,只在创建时设置,功能:共享
    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    // 外部状态通过方法传参,在生命周期内可以被多次修改(一般每次被获取时都会修改外部状态)
    public void setExtrinsicState(String extrinsicState) {
        this.extrinsicState = extrinsicState;
    }

    @Override
    public void operation() {
        System.out.println("Object address:" + System.identityHashCode(this));
        System.out.println("intrinsic State:" + this.intrinsicState);
        System.out.println("extrinsic State:" + this.extrinsicState);
    }
}

String常量池

JDK String常量池优化:在编译过程中出现的字符串引用均指向常量池,在运行阶段创建的字符串则重新在堆中创建一个字符串对象
JDK Integer和Long,均有常量池,为[-128,127],short和char也有

    public static void stringTest() {
        String s1 = "hello";
        // s1 == s2 true - 均从String常量池中获取到常量引用
        String s2 = "hello";

        // s1 == s3 true - 编译时优化,常量之间将发生合并
        String s3 = "he" + "llo";

        // s1 == s4 false - 在运行时才能触发new指令创建对象,
        String s4 = "hel" + new String("lo");

        // s1 == s5 false - 在运行时会触发new指令创建对象,此时,常量池中存在一个String对象,在堆中又创建一个新的String对象
        // s4 == s5 false - 运行时创建的两个对象,引用不相同。
        String s5 = new String("hello");

        // s1 == s6 true - intern:从常量池中获取对应字符串引用
        String s6 = s5.intern();

        String s7 = "h";
        String s8 = "ello";

        // s1 == s9 false - 只要字符串链接过程中存在变量,则必定是运行时创建
        String s9 = s7 + s8;

        // a == b true
        Integer a = Integer.valueOf(100);
        Integer b = 100;

        // c == d false
        Integer c = Integer.valueOf(1000);
        Integer d = 1000;
    }

传送门 - 连接池示例:工厂模式

posted @ 2020-11-28 23:38  祁奇  阅读(258)  评论(0编辑  收藏  举报