定义
运用共享技术有效的支持大量细粒度的对象。
两个状态
- 内蕴状态存储在享元内部,不会随环境的改变而有所不同,是可以共享的。
- 外蕴状态是不可以共享的,它随环境的改变而改变的,因此外蕴状态是由客户端来保持(因为环境的变化是由客户端引起的)。
和对象池的区别
- 对象池主要解决对象的复用问题,池中的每个对象都是可以替换的,从池中获取对象A和获取对象B对客户端而言都是一样的。
- 享元模式主要解决对象的共享问题,建立多个细粒度的可以进行共享的对象是其关注的重点。
UML
优点
- 减少运行时对象实例的个数,节省内存。
- 将许多“虚拟”对象的状态集中管理。
缺点
- 一旦你实现了它,单个的逻辑实现将无法拥有独立而不同的行为。
应用场景
- 系统中有大量对象且这些对象消耗大量内存。
- 这些对象的状态大部分可以外部化。
- 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
示例
我们考虑一个场景,当我们需要自己实现绘制艺术字时,每一个字符都会有下面的几个状态:1.字体轮廓(这部分就是最耗内存的部分,每个字体都会有大量的数据来记录该字体的样式);2.字体字符(标志该字体用来表示哪一个字符);3.字体大小;4.字体位置。
当我们使用该艺术字来呈现一堆文本时,如果每个字符都加载同一份轮廓则会对内存和GC造成压力。
我们会发现对于同一个字符来说,轮廓和字符都是一致的内部状态,而大小和位置则是会变动的外部状态;我们通过使用享元模式来节省内存的使用;
Java

1 import java.util.HashMap; 2 import java.util.Map; 3 4 public class Main 5 { 6 public static void main(String[] args) 7 { 8 String text = "Hello World! Hello Flyweight!"; 9 for (int i = 0; i < text.length(); i++) 10 { 11 char c = text.charAt(i); 12 FontFlyweight font = (FontFlyweight) FontFlyweightFactory.getFont(c); 13 font.size = (int) ((Math.random() * 20) + 7); 14 font.x = (int) ((Math.random() * 500)); 15 font.y = (int) ((Math.random() * 500)); 16 font.drawFont(); 17 } 18 FontFlyweightFactory.printFontCount(); 19 } 20 21 /** 22 * 字体享元接口 23 */ 24 public interface IFontFlyweight 25 { 26 /** 27 * 绘制当前字体 28 */ 29 void drawFont(); 30 } 31 32 /** 33 * 字体享元类 34 */ 35 public static class FontFlyweight implements IFontFlyweight 36 { 37 /** 38 * 不会改变可以共享的内部状态 39 */ 40 public final char symbol; 41 public final String font; 42 43 /** 44 * 频繁改变不可以共享的外部状态 45 */ 46 public int size; 47 public int x; 48 public int y; 49 50 public FontFlyweight(char symbol) 51 { 52 this.symbol = symbol; 53 //这里一般会从硬盘读取指定的美术字的字体样式 54 this.font = "自定义的美术字"; 55 } 56 57 @Override 58 public void drawFont() 59 { 60 //实际上绘制一个指定样式的矢量字符是非常复杂的,内存中需要保存该字符的所有矢量信息,会占用比较大的内存开销 61 System.out.println("在位置(" + x + ", " + y + ")处使用字体"" + font + ""绘制"" + size + ""号尺寸的字符"" + symbol + """); 62 } 63 } 64 65 /** 66 * 字体享元工厂 67 */ 68 public static class FontFlyweightFactory 69 { 70 private static Map<Character, IFontFlyweight> _map = new HashMap<>(); 71 72 /** 73 * 根据外部状态获取对应的享元对象 74 */ 75 public static IFontFlyweight getFont(char key) 76 { 77 if(_map.containsKey(key)) 78 { 79 return _map.get(key); 80 } 81 IFontFlyweight info = new FontFlyweight(key); 82 _map.put(key, info); 83 return info; 84 } 85 86 public static void printFontCount() 87 { 88 System.out.println("内存中存在的字体实例数量:" + _map.values().size()); 89 } 90 } 91 }