模式说明

某些对象相似度很高,只有少量字段或方法不同,当需要大量这类对象时,若为每一次需求都创建实例,内存开销会很大。此时可以提炼出这些对象的相同部分,作为所谓的享元,创建一个享元工厂类,持有享元实例。需要使用上述对象时,从享元工厂中获取该实例。若这些对象有不同部分,提取这些不同部分单独成类,使用时作为享元中的方法参数传入享元(使相同和不同部分结合)。这样无论使用多少次,对于享元而言,只有一个实例的开销。

本示例以网站为抽象享元,博客网站和电子商务网站为具体享元,用户为外部非享元。展示如何通过享元工厂类添加和获取享元,如何将非享元作为享元方法的参数传入到享元内。

结构

抽象享元类
被使用对象中提炼出的相同内容的集合,定义享元的抽象方法。
具体享元类
继承抽象享元类,实现抽象方法。
非享元类
需要使用但不能被提炼为享元内容的部分,单独成类,需要时传入享元。
享元工厂类
持有具体享元实例,有添加享元和获取享元的方法。

代码演示

  1. package com.yukiyama.pattern.structure;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. /**
  5. * 享元模式
  6. */
  7. public class FlyweightDemo {
  8. public static void main(String[] args) {
  9. // 声明享元工厂
  10. WebsiteFactory fa = new WebsiteFactory();
  11. // 声明具体享元博客网站和电子商务网站
  12. Website blog = new BlogWebsite("Blog");
  13. Website ec = new BlogWebsite("EC");
  14. // 向享元工厂中添加如上两种享元
  15. fa.addFlyweight(blog);
  16. fa.addFlyweight(ec);
  17. // 通过享元工厂,创建两个博客网站和两个电子商务网站
  18. Website blog1 = fa.getFlyweight("Blog");
  19. Website blog2 = fa.getFlyweight("Blog");
  20. Website ec1 = fa.getFlyweight("EC");
  21. Website ec2 = fa.getFlyweight("EC");
  22. // 将两个博客网站和两个电子商务网站分给不同的使用者
  23. blog1.use(new User("莫小言"));
  24. blog2.use(new User("金大庸"));
  25. ec1.use(new User("马风"));
  26. ec2.use(new User("刘强西"));
  27. // 如下均输出“true”,即通过享元工厂获取的多个享元以及
  28. // 工厂内持有的享元均为同一个。
  29. System.out.println(blog == blog1 && blog1 == blog2);
  30. System.out.println(ec == ec1 && ec1 == ec2);
  31. }
  32. }
  33. /**
  34. * 享元工厂类
  35. * 以Map数据结构持有享元,key为网站类型,value为Website实例。
  36. * 实现添加享元,获取享元的方法。
  37. * 客户端声明享元工厂后,需要继续创建享元并将其添加进享元工厂中。
  38. */
  39. class WebsiteFactory{
  40. private Map<String, Website> flyweights = new HashMap<>();
  41. public void addFlyweight(Website web) {
  42. if(!flyweights.containsKey(web.getCatagory())) {
  43. flyweights.put(web.getCatagory(), web);
  44. } else {
  45. System.out.println("已存在该享元。");
  46. }
  47. }
  48. public Website getFlyweight(String key) {
  49. if(!flyweights.containsKey(key)) {
  50. System.out.println("无此享元。");
  51. return null;
  52. } else {
  53. return flyweights.get(key);
  54. }
  55. }
  56. }
  57. /**
  58. * 享元抽象类
  59. * 声明享元的字段和相关方法。
  60. */
  61. abstract class Website{
  62. private String catagory;
  63. public Website(String catagory) {
  64. this.catagory = catagory;
  65. }
  66. public String getCatagory() {
  67. return catagory;
  68. }
  69. public abstract void use(User user);
  70. }
  71. /**
  72. * 具体享元类
  73. * 继承享元抽象类,实现抽象方法。
  74. * 如下是博客网站类。
  75. */
  76. class BlogWebsite extends Website{
  77. public BlogWebsite(String catagory) {
  78. super(catagory);
  79. }
  80. @Override
  81. public void use(User user) {
  82. System.out.printf("这是一个%s网站,提供文章发布服务。\n", getCatagory());
  83. System.out.printf("网站用户为%s。\n", user.getUser());
  84. }
  85. }
  86. /**
  87. * 具体享元类
  88. * 如下是电子商务网站类。
  89. */
  90. class ECWebsite extends Website{
  91. public ECWebsite(String catagory) {
  92. super(catagory);
  93. }
  94. @Override
  95. public void use(User user) {
  96. System.out.printf("这是一个%s网站,提供商品发布服务。\n", getCatagory());
  97. System.out.printf("网站用户为%s。\n", user.getUser());
  98. }
  99. }
  100. /**
  101. * 非享元类
  102. * 享元Website需要结合非享元User使用,例如对博客网站来说,他们可能共用
  103. * 相同的文章编辑器控件(作为享元的一部分),但各自使用的用户不同(非享元)。
  104. */
  105. class User{
  106. private String user;
  107. public User(String user) {
  108. this.user = user;
  109. }
  110. public String getUser() {
  111. return user;
  112. }
  113. }