享元模式介绍
享元模式(Flyweight Pattern)是结构型设计模式的一种。其实对象池的一种实现方式,通过缓存可共享的对象,来减少对象的创建,可以降低程序内存占用,提高程序性能。
享元模式定义
使用共享对象有效的支持大量细粒度的对象
享元模式的使用场景
- 系统中存在大量的相似对象。
- 细粒度的对象都具备接近的外部状态,而且内部状态与环境无关。
- 需要缓冲池的场景。
内部状态:对象中可以共享的状态,其不会随着环境变化。
外部状态:对象中不可以共享的状态,它们会随着环境的改变而变化。
享元模式的 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<String, Phone> 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.提高了系统的复杂度,需要分离出外部状态和内部状态,当然为了设备性能,这点必须做的。
网友评论