Design Patterns(十) Flyweight

Flyweight pattern is primarily used to reduce the number of objects created and to decrease memory footprint and increase performance. This type of design pattern comes under structural pattern as this pattern provides ways to decrease object count thus improving the object structure of application.

前言

    享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

享元模式

基本介绍:

1) 享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象;
2) 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个;
3) 享元模式能够解决 重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率;
4) 享元模式 经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式。

内部状态和外部状态:

比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一
点,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,当我们落子后,
落子颜色是定的,但位置是变化的,所以棋子坐标就是棋子的外部状态。

1) 享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分: 内部状态和 外部状态;
2) 内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变;
3) 外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态;
4) 举个例子:围棋理论上有361个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题。

享元模式-UML图:





享元模式-案例图:



代码:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import java.util.HashMap;
import java.util.Map;

/**
* @Auther: Arsenal
* @Date: 2020-03-16 20:28
* @Description: 享元模式
*/
public class Flyweight {
public static void main(String[] args) {
WebsiteFactory factory = new WebsiteFactory();
WebSite webSiteNews1 = factory.getWebSiteCategory("新闻");
webSiteNews1.use(new User("老王"));
WebSite webSiteNews2 = factory.getWebSiteCategory("新闻");
webSiteNews2.use(new User("小王"));
WebSite webSiteblog1 = factory.getWebSiteCategory("博客");
webSiteblog1.use(new User("老张"));
WebSite webSiteblog2 = factory.getWebSiteCategory("博客");
webSiteblog2.use(new User("小张"));

System.out.println("网站的分类共=" + factory.getWebSiteCount());

}
}

/**
* 享元工厂
*/
class WebsiteFactory {
Map<String, ConcreteWebsite> pool = new HashMap<>();

//根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回
public WebSite getWebSiteCategory(String type) {
if (!pool.containsKey(type)) {
pool.put(type, new ConcreteWebsite(type));
}
return pool.get(type);
}

//获取网站分类的总数 (池中有多少个网站类型)
public int getWebSiteCount() {
return pool.size();
}
}

/**
* 抽象的享元角色
*/
abstract class WebSite {
void use(User user) {
}
}

/**
* 具体的享元角色
*/
class ConcreteWebsite extends WebSite {

//共享的部分,内部状态
private String type; //网站发布的形式(类型)

ConcreteWebsite(String type) {
this.type = type;
}

@Override
void use(User user) {
System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());
}
}

class User {
String name;

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

总结

享元模式的注意事项和细节:

1) 在享元模式这样理解,“享”就表示共享,“元”表示对象;
2) 系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式;
3) 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储;
4) 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率;
5) 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们 使用享元模式需要注意的地方;
6) 使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制;
7) 享元模式经典的应用场景是需要缓冲池的场景,比如 String常量池、数据库连接池。

延伸

    享元模式
    享元模式-菜鸟教程
    享元模式(Flyweight Pattern)
    Design Patterns - Flyweight Pattern
    尚硅谷Java设计模式,韩顺平图解java设计模式

Content
  1. 1. 前言
  2. 2. 享元模式
  3. 3. 总结
  4. 4. 延伸