享元模式

对象结构型模式

模式动机

面向对象解决了灵活性和可扩展性的问题,但在很多时候都需要增加系统中类和对象个数,如果对象数量太多,可能导致性能下降。享元模式正是为了解决这一问题,通过共享技术实现相同或相似对象重用。

享元模式中可以共享的相同内容称为内部状态,而那些需要外部环境设置的不能共享的内容称为外部状态。通过这种内部外部状态的区分,可以设置不同的外部状态使得相同对象产生一些不同的特征,而相同的内部状态是共享的。

享元模式中通常会出现工厂模式,创建一个享元工厂来维护一个享元池,用于存储具有相同内部状态的享元对象。

实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象(细粒度对象),包含很少的内部状态。享元模式的目的就是使用共享技术大量复用细粒度对象。

应用

运用共享技术支持大量复用细粒度对象。系统只会使用少量对象,这些对象都很相似,状态变化很小,可以多次复用。

享元模式包含如下角色:

  • 抽象享元类Flyweight
  • 具体享元类ConcreteFlyweight
  • 非共享具体享元类UnsharedConcreteFlyweight
  • 享元工厂类FlyweightFactory

通过使用享元模式可以节省内存,提高系统性能

享元模式的核心在于享元工厂类,其作用是提供一个存储所有享元对象的享元池。用户需要对象时首先试图从享元池获取,如果获取失败则创建一个新的享元对象返回并在享元池中保存该新增对象。

典型享元工厂类代码如下:

public class FlyweightFactory {
private HashMap flyweights = new HashMap();
public Flyweight getFlyweight(String key) {
if(flyweights.containsKey(key)) {
return (Flyweight)flyweights.get(key);
} else {
Flyweight fw = new ConcreteFlyweight();
flyweights.put(key,fw);
return fw;
}
}
}

享元模式能做到共享的关键是区分内部状态和外部状态。内部状态不会随环境改变因此可以共享,外部状态随环境改变因此不可以共享。

典型享元类代码如下:

public class Flyweight {
//内部状态作为成员属性
private String intrinsicState;
public Flyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
public void operation(String extrinsicState) {
// ...
}
}

实例:共享网络设备(无外部状态)

很多网络设备都是支持共享的,如交换机、集线器等,多台终端计算机可以连接同一台网络设备,并通过该网络设备进行数据转发。

实例:共享网络设备(有外部状态)

虽然中心网络设备是共享的,但是每个终端计算机分配到的端口是不同的,因此多台计算机在共享设备时必须使用不同端口。这时可以把端口视作外部状态,需要时再进行设置。

优点

  1. 极大减少内存中对象的数量,相同或相似对象只需要保存一份
  2. 享元对象可以在不同环境中被共享,因为外部状态相对独立,不会影响内部状态

缺点

  1. 系统变得更复杂,需要分离内部和外部状态,这使得程序逻辑更复杂
  2. 需要把享元对象的一些状态外部化,但是读取外部状态需要更长运行时间

适用场合

  • 系统中存在大量相同或相似对象,避免反复实例化造成内存浪费
  • 对象的大部分状态可以外部化,并且可以把这些外部状态传入对象
  • 只有在多次重复使用享元对象时才值得用享元模式,因为使用享元模式需要维护一个享元池,这需要额外开销

JDK中的String类使用了享元模式(常量池?这其实是编译优化期做的):

public class Demo {
public static void main(String args[]) {
String str1 = "abcd";
String str2 = "abcd";
String str3 = "ab" + "cd";
String str4 = "ab";
str4 += "cd";
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str1 == str4); // true
}
}

模式扩展

单纯享元模式

在单纯享元模式中,所有享元对象都是可以共享的,即所有抽象享元类的子类都可以共享

复合享元模式

将一些单纯享元使用组合模式加以组合,可以形成复合享元对象,这样的复合享元本身不能共享,但是它们可以分解成单纯享元对象(可以共享)

联用其他模式

享元模式本身就需要依赖工厂模式返回享元对象,享元工厂类通常提供一个静态的工厂方法用于返回享元对象,使用简单工厂模式进行该设计。

通常一个系统只有一个享元工厂,所以享元工厂类可以使用单例模式设计

享元模式可以结合组合模式形成复合享元模式,统一对复合享元中的享元对象设置外部状态。

(•‿•)