运用共享技术来有效地支持大量细粒度对象的复用,通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。一般不变类都会使用享元模式。
享元模式与单例模式的区别:
- 单例模式中一个类只能创建一个对象。
- 享元模式中一个类可以创建多个对象,只不过可以采用工厂等方式共享同一个对象,并且可以控制对象创建或使用的数量。比如数据库连接池,不是单例模式示例,而是享元模式示例。
优点:极大减少内存中相似或相同对象数量,节约系统资源,提供系统性能。
缺点:需要关注内/外部状态,代码更加复杂、如果对象复用度低,则不适用与享元模式。
核心思想:如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用都创建新的对象。在享元模式中,由于需要构造和维护这些可以共享的对象,因此,常会出现一个工厂类,用于维护和创建对象。
享元模式的主要角色由享元工厂、抽象享元、具体享元类和主函数几部分组成。
享元工厂:用于创建具体享元类,维护相同的享元对象。它保证相同的享元对象可以被系统共享。即,其内部使用了类似单例模式的方法,当请求对象已经存在时,直接返回对象,不存在时,在创建对象。享元工厂是享元模式的核心,它需要确保系统可以共享相同的对象。一般情况下,享元工厂会维护一个对象集合,当任何组件尝试获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类:若没有,则创建一个新的享元对象,并将它加入到维护队列中。抽象享元:定义需要共享的对象业务接口。享元类被创建出来总是为了实现某些特定的业务逻辑,而抽象享元便定义这些逻辑的语义行为。具体享元类:实现抽象享元类的接口,完成某一具体逻辑。客户端:使用享元模式的组件,通过享元工厂取得享元对象。
享元模式经典的应用场景就是池技术了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式
Integer 类使用了享元模式,通过Integer.valueOf()这个静态工厂方法创建Integer实例,当传入的int范围在-128~`+127之间时,会直接返回缓存的Integer`实例(自动装箱时底层会使用 Integer.valueOf())。
1 | public final class Integer extends Number implements Comparable<Integer> { |