一、前言

本篇主题为结构型模式中的第六个模式–享元模式。上篇 Java 设计模式主题为《Java 设计模式之外观模式(十)》

二、简单介绍

# 2.1 定义

享元(Flyweight)模式是构造型模式之一, 它通过与其他类似对象共享数据来减小内存占用。

# 2.2 参与角色

  1. 抽象享元:所有具体享元类的父类,规定一些需要实现的公共接口,可接收外部状态。

  2. 具体享元:抽象享元角色的具体实现类,并实现了抽象享元角色规定的方法并存储内部状态。

  3. 享元工厂:负责创建和管理享元角色。

# 2.3 应用场景

  1. 一个应用程序使用了大量的对象。

  2. 由于使用大量的对象,造成很大的存储开销。

  3. 对象的大多数状态都可变为外部状态。

其中,在享元对象内部并不会随环境变化而改变的共享部分,称为内部状态。反之,随环境改变而改变的、不可以共享的状态称为外部状态。

三、实现方式

以网站为例,小明是个自由职业者,在网上专门帮人完成软件设计(网站后台管理系统)需求。

在不使用享元模式时,通过代码实现上述场景需求:

网站系统:

  1. public class WebSite {
  2. private String name;
  3. public WebSite(String name) {
  4. super();
  5. this.name = name;
  6. }
  7. public void show() {
  8. System.out.println(this.name);
  9. }
  10. }

客户端:

  1. public class Client {
  2. public static void main(String[] args) {
  3. WebSite ws1 = new WebSite("A后台管理系统");
  4. WebSite ws2 = new WebSite("B后台管理系统");
  5. ws1.show();
  6. ws2.show();
  7. }
  8. }

打印结果:

  1. A后台管理系统
  2. B后台管理系统

从代码上看,当有 N 家公司找小明做后台管理系统时,小明需要编写 N 次代码。假设某一个系统出现 bug,意味着需要维护 N 个系统的代码,维护量很大。因此,上边的代码实现方案不可取。

用过后台管理系统的网友都知道,后台系统的功能大同小异,页面的结构和展示可以相同(内部状态),不同之处在于公司业务需求(外部状态)。

现在,我们使用享元模式实现:

外部状态:

  1. public class User {
  2. private String name;
  3. public User(String name) {
  4. super();
  5. this.name = name;
  6. }
  7. public String getName() {
  8. return name;
  9. }
  10. }

抽象享元:

  1. public abstract class WebSite {
  2. // 内部状态
  3. protected String name;
  4. public abstract void show(User user);
  5. }

具体享元:

  1. class WebSiteA extends WebSite {
  2. public WebSiteA(String name) {
  3. this.name = name;
  4. }
  5. @Override
  6. public void show(User user) {
  7. System.out.println(user.getName() + "的" + this.name);
  8. }
  9. }

享元工厂:

  1. public class WebSiteFactory {
  2. private static Map<String,WebSite> map = new HashMap<String,WebSite>();
  3. public static WebSite getWebSite(String type) {
  4. if (!map.containsKey(type)) {
  5. map.put(type, new WebSiteA(type));
  6. }
  7. return map.get(type);
  8. }
  9. }

客户端:

  1. public class Client {
  2. public static void main(String[] args) {
  3. WebSite ws1 = WebSiteFactory.getWebSite("后台管理系统");
  4. WebSite ws2 = WebSiteFactory.getWebSite("后台管理系统");
  5. System.out.println(ws1 == ws2);
  6. ws1.show(new User("A 公司"));
  7. ws2.show(new User("B 公司"));
  8. }
  9. }

打印结果:

  1. true
  2. A 公司的后台管理系统
  3. B 公司的后台管理系统

从结果可知,两家公司通用一套系统模板,很大的节省了小明的开发量和维护量。

总结:享元模式可以避免大量非常相似的类开销。

UML 类图表示如下: