一、前言

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

二、简单介绍

2.1 定义

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

2.2 参与角色

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

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

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

2.3 应用场景

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

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

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

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

三、实现方式

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

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

网站系统:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class WebSite {

private String name;

public WebSite(String name) {
super();
this.name = name;
}

public void show() {
System.out.println(this.name);
}
}

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Client {

public static void main(String[] args) {

WebSite ws1 = new WebSite("A后台管理系统");

WebSite ws2 = new WebSite("B后台管理系统");

ws1.show();

ws2.show();

}
}

打印结果:

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

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

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

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

外部状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class User {

private String name;

public User(String name) {
super();
this.name = name;
}

public String getName() {
return name;
}

}

抽象享元:

1
2
3
4
5
6
public abstract class WebSite {
// 内部状态
protected String name;

public abstract void show(User user);
}

具体享元:

1
2
3
4
5
6
7
8
9
10
11
12
class WebSiteA extends WebSite {

public WebSiteA(String name) {
this.name = name;
}

@Override
public void show(User user) {
System.out.println(user.getName() + "的" + this.name);
}

}

享元工厂:

1
2
3
4
5
6
7
8
9
10
11
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);
}
}

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 公司"));
}
}

打印结果:

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

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

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

UML 类图表示如下: