首页 > 编程笔记 > Java笔记 阅读:12

享元模式(Java实现)

享元模式属于经典的 GoF 设计模式,它让程序能够尽量共用相似的对象而不是去创建对的象,以求降低内存占用量或计算开销。

刚开发出来的应用程序可能会使用大量的对象,但在这些对象里并非每一个都是用户所必需的。由于程序中已经存在的实例比较多,而且还会不断地创建新实例,因此内存开销相当大。

其实在许多场合,我们都可以考虑用一小群实例来取代目前的这一大批实例。这一小群实例能够在程序的各部分代码之间方便地共用。于是,垃圾收集算法的压力就会减少。另外,如果程序采用这样的方式进行通信,那么需要开启的 socket 数量也会降低。

享元模式在JDK中的运用

在 JDK 里很容易就能找到享元模式,只是有些人可能不太注意。

例如,在 java.base 模块的 java.lang 包里,有许多针对原始类型而设立的包装器类型,这些类型就会利用享元模式降低内存用量。如果程序要处理许多重复的值,那么享元模式的好处尤为明显。

Integer、Byte 与 Character 这样的包装器类都提供 valueOf 的方法,用来获取与相应的原始类型值对应的包装器对象,这个方法用内部缓存来放置重复的对象,而不是每次都创建新的对象,所以说,它使用了享元模式。

享元模式范例

举个例子,假设车库里面有几种车辆可供租借。除了要管理用于出租的汽车本身,还需要管理与每辆车相关的文档,而每一种车的文档在默认情况下都是相似的。

因此,我们可以复用这些文档,只有在首次遇见用户需要租借某种车辆时,我们才创建一个新的文档并将其纳入缓存。如果用户租借的是一种以前有人借过的车,那么直接把当时创建的那个文档取出来复用即可。

【实例】通过享元模式在程序里共享同一种车的文档模板,以降低程序的内存用量。
public static void main(String[] args) {
    System.out.println("Pattern Flyweight, sharing templates");
    Vehicle car1 = VehicleGarage.borrow("sport");
    car1.move();
    Vehicle car2 = VehicleGarage.borrow("sport");
    System.out.println("Similar template:" + (car1.equals(car2)));
}
程序输出结果如下:
Pattern Flyweight, sharing vehicles
VehicleGarage, borrowed type:sport
Vehicle, type:'sport-car', confirmed
VehicleGarage, borrowed type:sport
Similar template: true
这个范例的关键是接下来要展示的 VehicleGarage 类,该类在内部实现了一套缓存,用来登记并存储与每一种车相对应的文档模板。

【实例】 VehicleGarage 类实现一套内部缓存,让程序只在找不到某种车的文档模板时才去创建这样的模板,以降低这些模板占据的内存量。
class VehicleGarage {
    private static final Map<String, Vehicle> vehicleByType = new HashMap<>();
    static {
        vehicleByType.put("common", new VehicleType("common-car"));
        vehicleByType.put("sport", new VehicleType("sport-car"));
    }

    private VehicleGarage() {
    }

    static Vehicle borrow(String type) {
        Vehicle v = vehicleByType.get(type);
        if (v == null) {
            v = new VehicleType(type);
            vehicleByType.put(type, v);
        }
        System.out.println("VehicleGarage, borrowed type:" + type);
        return v;
    }
}
下面是范例各类之间的关系图:


图 1 VehicleGarage提供的VehicleType享元与用户需要关注的Vehicle接口之间的关系

由图中可以看出,用户不需要关注程序中的 VehicleType 类,尽管 VehicleGarage 所提供的 Vehicle 享元其实是 VehicleType 类型的实例,但用户还是只需要通过 Vehicle 接口本身来操纵这些实例。

总结

享元模式的主要优点在于能够根据用户的要求,管理一大批与这些要求有关的对象。

享元模式会根据需求来创建对象,让开发者能够很好地控制当前已经创建出的这些实例。用户只需要获取并使用这些享元就好,而无须担心因为对象的身份不同而引发问题。

享元模式让用户能够便捷地获取对象,并在这些对象上面使用由底层代码所支持的一些功能,这样做符合 SOLID 设计理念与 DRY 原则。

相关文章