享元模式
一、定义
摘自百度百科: 它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。
点击查看源码
二、角色分类
抽象享元类(Flyweight)
通常是一个接口或抽象类,其声明了具体享元类的公共方法,这些方法可以向外界提供享元对象的内部数据或内部状态,同时也可以通过这些方法来设置外部数据或外部状态
具体享元类(Concrete Flyweight)
它实现了抽象享元类,它的实例被称为享元对象;在具体享元类内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
非共享具体享元类(Unshared Concrete Flyweight)
不能被共享的子类可被设计为非共享具体享元类,当需要一个非共享具体享元类的对象时可以直接通过实例化创建
享元工厂类(Flyweight Factory)
该类主要用于创建和管理享元对象,它针对抽象享元类变成,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储键值对的集合(也可以是其他类型的集合),可以结合工厂模式进行设计
客户角色(Client)
具体调用方法的角色
三、实现方式
UML图
![未命名文件 (1)](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)
单纯享元模式
抽象享元类(Flyweight)
1 2 3
| public interface Flyweight { public void operate(String type); }
|
具体享元类(Concrete Flyweight)和非共享具体享元类(Unshared Concrete Flyweight)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class CharacterFlyWeight implements Flyweight { private String name;
public CharacterFlyWeight(String name) { this.name = name; }
@Override public void operate(String type) { System.out.println("姓名 = " + name); System.out.println("属性 = " + type); } }
|
享元工厂(Flyweight Factory)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class FlyweightFactory { private Map<String, Flyweight> characterPool = new HashMap<>();
public Flyweight factory(String user) { Flyweight flyweight = characterPool.get(user); if (null == flyweight) { flyweight = new CharacterFlyWeight(user); characterPool.put(user, flyweight); } return flyweight; } }
|
客户角色(Client)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Client { public static void main(String[] args) { FlyweightFactory factory = new FlyweightFactory(); Flyweight flyweightA = factory.factory("小A"); flyweightA.operate("文静");
Flyweight flyweightB = factory.factory("小B"); flyweightB.operate("腼腆");
Flyweight flyweightC = factory.factory("小A"); flyweightC.operate("活泼");
System.out.println(flyweightA == flyweightC); } }
|
运行结果
1 2 3 4 5 6 7
| 姓名 = 小A 属性 = 文静 姓名 = 小B 属性 = 腼腆 姓名 = 小A 属性 = 活泼 true
|
复合享元模式
抽象享元角色(Flyweight)
1 2 3
| public interface Flyweight { public void operate(String type); }
|
具体享元角色(Concrete Flyweight)和非共享具体享元角色(Unshared Concrete Flyweight)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class CharacterFlyweight implements Flyweight { private String name;
public CharacterFlyweight(String name) { this.name = name; }
@Override public void operate(String type) { System.out.println("姓名 = " + name); System.out.println("属性 = " + type); } }
|
复合享元角色(Composite Concrete Flyweight)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class CharacterCompositeFlyweight implements Flyweight { private Map<String, Flyweight> files = new HashMap<>();
public void add(String key, Flyweight flyweight) { files.put(key, flyweight); }
@Override public void operate(String type) { Flyweight flyweight; for (String key : files.keySet()) { flyweight = files.get(key); flyweight.operate(type); } } }
|
享元工厂角色(Flyweight Factory)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class FlyweightFactory { private Map<String, Flyweight> characterPool = new HashMap<>();
public Flyweight factory(List<String> compositeState) { CharacterCompositeFlyweight compositeFlyweight = new CharacterCompositeFlyweight(); compositeState.forEach(any -> compositeFlyweight.add(any, this.factory(any))); return compositeFlyweight; }
public Flyweight factory(String user) { Flyweight flyweight = characterPool.get(user); if (null == flyweight) { flyweight = new CharacterFlyweight(user); characterPool.put(user, flyweight); } return flyweight; } }
|
客户角色(Client)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class Client { public static void main(String[] args) { List<String> compositeState = new ArrayList<String>(); compositeState.add("小A"); compositeState.add("小B"); compositeState.add("小C"); compositeState.add("小B"); compositeState.add("小A");
FlyweightFactory flyFactory = new FlyweightFactory(); Flyweight compositeFly1 = flyFactory.factory(compositeState); Flyweight compositeFly2 = flyFactory.factory(compositeState); compositeFly1.operate("梦游中...");
System.out.println("---------------------------------"); System.out.println("复合享元模式是否可以共享对象:" + (compositeFly1 == compositeFly2));
String user = "小A"; Flyweight fly1 = flyFactory.factory(user); Flyweight fly2 = flyFactory.factory(user); System.out.println("单纯享元模式是否可以共享对象:" + (fly1 == fly2)); } }
|
运行结果
1 2 3 4 5 6 7 8 9
| 姓名 = 小B 属性 = 梦游中... 姓名 = 小A 属性 = 梦游中... 姓名 = 小C 属性 = 梦游中... --------------------------------- 复合享元模式是否可以共享对象:false 单纯享元模式是否可以共享对象:true
|
四、应用场景
以下部分内容摘自菜鸟教程
意图: 运用共享技术有效地支持大量细粒度的对象。
主要解决: 在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
何时使用:
- 系统中有大量对象。
- 这些对象消耗大量内存。
- 这些对象的状态大部分可以外部化。
- 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
- 系统不依赖于这些对象身份,这些对象是不可分辨的。
如何解决: 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。
关键代码: 用 HashMap 存储这些对象。
应用实例:
- JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
- 数据库的连接池。
使用场景:
- 系统有大量相似对象。
- 需要缓冲池的场景。
注意事项:
- 注意划分外部状态和内部状态,否则可能会引起线程安全问题。
- 这些类必须有一个工厂对象加以控制。
五、优缺点
优点
大大减少对象的创建,降低系统的内存,使效率提高。
缺点
提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。