定义

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

结构和说明

结构型模式-享元模式 - 图1

示例代码

  1. public class FlyweightDemo {
  2. /**
  3. * 享元接口,通过这个接口享元可以接受并作用于外部状态
  4. */
  5. public static interface Flyweight {
  6. /**
  7. * 示例操作,传入外部状态
  8. *
  9. * @param extrinsicState
  10. * 示例参数,外部状态
  11. */
  12. void operation(String extrinsicState);
  13. }
  14. /**
  15. * 享元对象
  16. */
  17. public static class ConcreteFlyweight implements Flyweight {
  18. /**
  19. * 示例,描述内部状态
  20. */
  21. private String intrinsicState;
  22. public ConcreteFlyweight(String state) {
  23. this.intrinsicState = state;
  24. }
  25. @Override
  26. public void operation(String extrinsicState) {
  27. // 具体的功能处理,可能会用到享元内部、外部的状态
  28. }
  29. }
  30. /**
  31. * 不需要共享的 Flyweight 对象
  32. *
  33. * 通常是将被共享的享元对象作为子节点组合出来的对象
  34. */
  35. public static class UnsharedConcreteFlyweight implements Flyweight {
  36. /**
  37. * 示例,描述对象的状态
  38. */
  39. private String allState;
  40. @Override
  41. public void operation(String extrinsicState) {
  42. // 具体的功能处理
  43. }
  44. }
  45. /**
  46. * 享元工厂
  47. */
  48. public static class FlyweightFactory {
  49. /**
  50. * 缓存多个 Flyweight 对象,这里这是示意一下
  51. */
  52. private Map<String, Flyweight> fsMap = new HashMap<>();
  53. /**
  54. * 获取 key 对应的享元对象
  55. *
  56. * @param key
  57. * 获取享元对象的 key, 只是示意
  58. * @return key 对应的享元对象
  59. */
  60. public Flyweight getFlyweight(String key) {
  61. // 这个方法中基本实现步骤如下:
  62. // 1: 先从缓存中找,是否存在 key 对应的 Flyweight 对象
  63. Flyweight f = fsMap.get(key);
  64. // 2:如果存在就返回相应的 Flyweight 对象
  65. if (f == null) {
  66. // 3:如果不存在
  67. // 3.1: 创建一个新的 Flyweight 对象
  68. f = new ConcreteFlyweight(key);
  69. // 3.2: 把这个新的 Flyweight 对象添加到缓存中
  70. fsMap.put(key, f);
  71. // 3.3: 然后返回这个新的 Flyweight 对象
  72. }
  73. return f;
  74. }
  75. }
  76. /**
  77. * Client 对象,通常会维护一个对 Flyweight 的引用, 计算或存储一个或多个 Flyweight 的外部状态
  78. */
  79. public static class Client {
  80. // 具体的功能处理
  81. }
  82. }

调用顺序

享元模式的使用上,有两种情况,一种是没有“不需要共享”的享元对象,就如同前面的示例那样,只有共享享元对象的情况;还有一种既有共享享元对象,又有不需要共享的享元对象的情况。

只有共享享元对象的情况下

结构型模式-享元模式 - 图2

优缺点

优点

  • 减少对象数量,节省内存空间。

缺点

  • 维护共享对象,需要额外开销。

思考

本质

分离和共享。

何时选用

  • 如果一个应用程序使用了大量的细粒度对象,可以使用享元模式来减少对象数量。
  • 如果由于使用大量的对象,造成很大的存储开销,可以使用享元模式来减少对象数量,并节约内存。
  • 如果对象的大多数状态都可以转变为外部状态,比如通过计算得到,或者是从外部传入等,可以使用享元模式来实现内部状态和外部状态的分离。
  • 如果不考虑对象的外部状态,可以使用相对较少的共享对象取代很多组合对象,可以使用享元模式来共享对象,然后组合对象来使用这些共享对象。

相关模式

  • 享元模式与单例模式

这两个模式可以组合使用。
通常情况下,享元模式中的享元工厂可以实现成为单例。另外,享元工厂中缓存的享元对象,都是单实例的,可以看成是单例模式的一种变形控制,在享元工厂中来单例享元对象。

  • 享元模式与组合模式

这两个模式可以组合使用。
在享元模式中,存在不需要共享的享元实现,这些不需要共享的享元通常是对共享的享元对象的组合对象。也就是说,享元模式通常会和组合模式使用,来实现更复杂的对象层次结构。

  • 享元模式和状态模式

这两个模式可以组合使用。
可以使用享元模式来共享状态模式中的状态对象。通常在状态模式中,会存在数量很大,细粒度的状态对象,而且他们基本上是可以重复使用的,都是用来处理某一个固定的状态的,它们需要的数据通常都是由上下文传入,也就是变化部分都分离出去了,所以可以用享元模式来实现这些状态对象。

  • 享元模式与策略模式

这两个模式剋组合使用。
可以使用享元模式来实现策略模式中的策略对象。和状态模式一样,在策略模式中也存在大量细粒度的策略对象,它们需要的数据同样是从上下文传入的,所以可以使用享元模式来实现这些策略对象。