设计模式-享元模式

享元模式介绍

享元模式(Flyweight Pattern)是结构型设计模式的一种。其实对象池的一种实现方式,通过缓存可共享的对象,来减少对象的创建,可以降低程序内存占用,提高程序性能。

享元模式定义

使用共享对象有效的支持大量细粒度的对象

享元模式的使用场景

  1. 系统中存在大量的相似对象。
  2. 细粒度的对象都具备接近的外部状态,而且内部状态与环境无关。
  3. 需要缓冲池的场景。

内部状态:对象中可以共享的状态,其不会随着环境变化。
外部状态:对象中不可以共享的状态,它们会随着环境的改变而变化。

享元模式的 UML 类图


角色介绍:

  • Flyweight:享元对象抽象类。
  • ConcreteFlyweight:具体享元对象。
  • FlyweightFactory:享元工厂,负责管理享元对象池和创建享元对象。

享元模式的简单实现

这里某东出售手机为例,每个用户选择手机后都生成手机商品对象显然耗费很多资源,甚至造成 OOM,我们就可以采用享元模式优化。

抽象享元角色

抽象享元角色是一个商品接口,它定义了showGoodsPrice方法用来展示商品的价格:

public interface IPrice {
    public void showGoodsPrice(String version);
}

具体享元角色

public class Phone implements IPrice {
    public String name;
    public String version;
    public int price;

    public Phone(String name) {
        this.name = name;
    }

    @Override
    public void showGoodsPrice(String version) {
        this.version = version;
        price = queryPrice(version);
        System.out.println("手机 " + name + " 存储版本为 " + version + ",售价为:" + price);
    }

    private int queryPrice(String version) {
        switch (version) {
            case "128G":
                return 5000;
            case "256G":
                return 6000;
        }
        return 99999;
    }
}

其中 name 属于内部状态,version 和 price 属于外部状态。showGoodsPrice方法根据手机存储 version 的不同会打印出不同的价格。

享元工厂

public class PhoneFactory {
    private static Map sPhoneMap = new HashMap<>();

    public static Phone getPhone(String name) {
        Phone ret = null;
        if (sPhoneMap.containsKey(name)) {
            System.out.println("使用缓存,key 为" + name);
            ret = sPhoneMap.get(name);
        } else {
            System.out.println("创建对象,key 为" + name);
            ret = new Phone(name);
            sPhoneMap.put(name, ret);
        }
        return ret;
    }
}

享元工厂PhoneFactory 用来创建 Phone 对象。通过Map容器来存储 Phone 对象,将内部状态 name 作为Map的key,以便标识 Phone 对象。如果Map容器中包含此key,则使用Map容器中存储的 Phone 对象,否则就新创建 Phone 对象,并放入Map容器中。

客户端调用

public class Client {
    public static void main(String[] args) {
        Phone phone1 = PhoneFactory.getPhone("HUAWEI mate30");
        phone1.showGoodsPrice("128G");
        Phone phone2 = PhoneFactory.getPhone("HUAWEI mate30");
        phone2.showGoodsPrice("256G");
    }
}

输出结果:

创建对象,key 为HUAWEI mate30
手机 HUAWEI mate30 存储版本为 128G,售价为:5000
使用缓存,key 为HUAWEI mate30
手机 HUAWEI mate30 存储版本为 256G,售价为:6000

从输出结果可以看到,只有第一次查询手机创建了一次对象,后续的查询都使用的是对象池中的对象。该例子中内存状态就是手机名称,在查询HUAWEI mate30 时内部状态不会发生变化;外部状态就是存储版本和价格,价格会随着存储版本不同而变化。通过缓存较少了内存占用,降低了gc 回收的次数,从而性能大大提高。

总结

享元模式优点
1.大大减少对象的创建,降低系统的内存,减少 GC ,提高性能。
享元模式缺点
1.提高了系统的复杂度,需要分离出外部状态和内部状态,当然为了设备性能,这点必须做的。

Android 源码中的享元模式

见Android 消息机制 Message 中 obtain() 和 recycle() 方法

你可能感兴趣的:(设计模式-享元模式)