一、前言

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

二、简单介绍

2.1 定义

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

2.2 参与角色

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

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

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

2.3 应用场景

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

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

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

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

三、实现方式

以网站为例,小明是个自由职业者,在网上专门帮人完成软件设计(网站后台管理系统)需求。
在不使用享元模式时,通过代码实现上述场景需求:
网站系统:

public class WebSite {
    private String name;
    public WebSite(String name) {
        super();
        this.name = name;
    }
    public void show() {
        System.out.println(this.name);
    }
}

客户端:

public class Client {
    public static void main(String[] args) {
        WebSite ws1 = new WebSite("A后台管理系统");
        WebSite ws2 = new WebSite("B后台管理系统");
        ws1.show();
        ws2.show();
    }
}

打印结果:

A后台管理系统
B后台管理系统

从代码上看,当有 N 家公司找小明做后台管理系统时,小明需要编写 N 次代码。假设某一个系统出现 bug,意味着需要维护 N 个系统的代码,维护量很大。因此,上边的代码实现方案不可取。
用过后台管理系统的网友都知道,后台系统的功能大同小异,页面的结构和展示可以相同(内部状态),不同之处在于公司业务需求(外部状态)。
现在,我们使用享元模式实现:
外部状态:

public class User {
    private String name;
    public User(String name) {
        super();
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

抽象享元:

public abstract class WebSite {
    // 内部状态
    protected String name;
    public abstract void show(User user);
}

具体享元:

class WebSiteA extends WebSite {
    public WebSiteA(String name) {
        this.name = name;
    }
    @Override
    public void show(User user) {
        System.out.println(user.getName() + "的" + this.name);
    }
}

享元工厂:

public class WebSiteFactory {
    private static Map<String, WebSite> map = new HashMap<String, WebSite>();
    public static WebSite getWebSite(String type) {
        if (!map.containsKey(type)) {
            map.put(type, new WebSiteA(type));
        }
        return map.get(type);
    }
}

客户端:

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

打印结果:

true
A 公司的后台管理系统
B 公司的后台管理系统

从结果可知,两家公司通用一套系统模板,很大的节省了小明的开发量和维护量。
总结:享元模式可以避免大量非常相似的类开销。
UML 类图表示如下:

作者:月光中的污点

来源:https://www.extlight.com/2017/11/21/Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F%EF%BC%88%E5%8D%81%E4%B8%80%EF%BC%89/