设计模式-享元

  1. 意图
  2. 例子
  3. 适用性
  4. 结构
  5. 协作
  6. 效果

意图

运用共享技术有效地支持大量细粒度的对象。

例子

面向对象编程,在面对事物时会针对面向对象建立映射。
但是如果同类的对象使用过多,就会面临内存占用率过高,产生难以预估的内存消耗。为此,享元模式,描述了如何共享对象,使得可以使用他们并无需高昂的代价。
例如需要提供一个富文本编。

public interface Glyph {
public void draw();
public void setFont();
}

上面是具备绘图功能。下面就负责存储富文本的每一个字符。

public class Character implement Glyph {
public Character(char) {

}

public void draw() {

}

public void setFont() {

}
}

当然,这里不仅仅是字符具备该功能,比如一些线条等。

为了避免每一个字体属性都分配存储空间,可以将该属性存储到外部缓存中。

public class GlyphContext {
List<Font> fonts;
// add
// remove
// get
}

接下来就需要提供一个工厂,他来负责构建一些Glyph,当需要字体属性时,就去存储空间去找;不需要,则直接创建即可:
public class GlyphFactory {
public List<Character> chars;
public Glyph createCharacter(char) {
if (!chars.has(char)) {
char.add(new Character(char));
}
return chars.get(char);
}
pubic GlyphContext context;
}

适用性

享元模式的有效性很大程度上取决于如何使用它以及在何处使用它。以下情况都成立使用:

  1. 一个应用程序使用了大量的对象
  2. 完全由于使用大量的对象,造成很大的存储开销
  3. 对象的大多数状态都可变为外部状态
  4. 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象
  5. 应用程序不依赖于对象标识。由于flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值

结构

github

协作

  1. flyweight 执行时所需的状态必定是内部的或外部的。内部状态存储于 ConcreteFlyweight 对象之中;而外部对象则由 Client 对象存储或计算。当用户调用 flyweight 对象的操作时,将该状态传递给它。
  2. 用户不应直接对 ConcreteFlyweight 类进行实例化,而只能从 FlyweightFactory 对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享。

效果

使用 flyweight 模式时,传输、查找和/或计算外部状态都会产生运行时的开销,尤其当 flyweight 原先被存储为内部状态时。然而,空间上的节省抵消了这些开销。共享的 flyweight 越多,空间节省也就越大。
存储节约由以下几个因素决定:

  1. 因为共享,实例总数减少的数目
  2. 对象内部状态的平均数目
  3. 外部状态是计算的还是存储的

共享的 flyweight 越多,存储节约也就越多。节约量随着共享状态的增多而增大。当对象使用大量的内部及外部状态,并且外部状态是计算出来的而非存储的时候,节约量将达到最大。所以,可以用两种方法来节约存储:用共享减少内部状态的消耗,用计算时间换取对外 部状态的存储。
flyweight 模式经常和 Composite模式结合起来表示一个层次式结构,这一层次式结构是一个共享叶节点的图。共享的结果是,Flyweight的叶节点不能存储指向父节点的指针。而父节点的指针将传给Flyweight作为它的外部状态的一部分。这对于该层次结构中对象之间相互通讯的方式将产生很大的影响。