在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈。
例如,围棋和五子棋中的黑白棋子,图像中的坐标点或颜色,教室里的桌子和凳子等。这些对象有很多相似的地方,如果能把它们相同的部分提取出来共享,则能节省大量的系统资源,这就是享元模式的产生背景。

享元模式的定义与特点

享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式的主要优点是:
相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
其主要缺点是:

  1. 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
  2. 读取享元模式的外部状态会使得运行时间稍微变长。

    享元模式的结构与实现

    享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。
  • 内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变;
  • 外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。

比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。
享元模式其实是工厂方法模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能。

实现

  1. //非享元角色
  2. public class UnsharedFlyweight
  3. {
  4. private string info;
  5. public UnsharedFlyweight(string info)
  6. {
  7. this.info = info;
  8. }
  9. public string getInfo()
  10. {
  11. return info;
  12. }
  13. public void setInfo(string info)
  14. {
  15. this.info = info;
  16. }
  17. }
  18. //抽象享元角色
  19. public interface Flyweight
  20. {
  21. void operation(UnsharedFlyweight state);
  22. }
  23. //具体享元角色
  24. class ConcreteFlyweight : Flyweight
  25. {
  26. private string key;
  27. public ConcreteFlyweight(string key)
  28. {
  29. this.key = key;
  30. Debug.Log("具体享元角色"+key+"被创建");
  31. }
  32. public void operation(UnsharedFlyweight outState)
  33. {
  34. Debug.Log("具体享元角色" + key + "被调用");
  35. Debug.Log("非享元信息是:" + outState.getInfo());
  36. }
  37. }
  38. //享元工厂
  39. public class FlyweightFactory
  40. {
  41. private Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>();
  42. public Flyweight getFlyweight(string key)
  43. {
  44. Flyweight flyweight;
  45. if (flyweights.ContainsKey(key))
  46. {
  47. Debug.Log("具体享元" + key + "已存在,获取成功");
  48. }
  49. else {
  50. flyweight = new ConcreteFlyweight(key);
  51. flyweights.Add(key, flyweight);
  52. }
  53. return flyweights[key];
  54. }
  55. }
  1. void Start()
  2. {
  3. FlyweightFactory factory = new FlyweightFactory();
  4. Flyweight flyweight1 = factory.getFlyweight("a");
  5. Flyweight flyweight2 = factory.getFlyweight("a");
  6. flyweight1.operation(new UnsharedFlyweight("第一次调用a"));
  7. flyweight2.operation(new UnsharedFlyweight("第二次调用a"));
  8. }
  9. //执行结果
  10. //具体享元角色a被创建
  11. //具体享元a已存在,获取成功
  12. //具体享元角色a被调用
  13. //非享元信息是:第一次调用a
  14. //具体享元角色a被调用
  15. //非享元信息是:第二次调用a

享元模式的应用场景

  1. 系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
  2. 大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
  3. 由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。

    享元模式的扩展

    http://c.biancheng.net/view/1371.html