设计模式(19)——享元模式

本文介绍享元模式的概念和应用。

基本思想和原则

使用共享对象可有效地支持大量的细粒度的对象。

动机

当系统中有大量相似对象存在,它们具有一定的共性,但又有自己特殊的地方,我们可以利用享元模式将这些共性提炼成对象的外部特征,预先创建出这些对象,之后需要时可以直接取出使用,然后再赋予其非共性的属性。

实现

数据库连接池就是一个典型的享元模式的应用,预先创建一些数据库连接,当需要时直接取用,而不是立刻创建一个,这种池技术可以有效降低系统开销。

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
public class DBConnection {
private int id;

public DBConnection(int id) {
this.id = id;
}

public int getId() {
return this.id;
}
}

public class DBConnectionFactory {
private static Vector<DBConnection> pool = new Vector<DBConnection>();
private static Vector<Integer> connectionStates = new Vector<Integer>();

public static void init(int n) {
for(int i = 0; i < n; i++) {
DBConnection conn = new DBConnection(i);
pool.add(conn);
connectionStates.add(0);
}
}

public static DBConnection getDBConnection() {
for(int i = 0; i < pool.size(); i++) {
if (connectionStates.get(i).equals(0)) {
connectionStates.set(i, 1);
return pool.get(i);
}
}
return null;
}

public static void releaseDBConnection(DBConnection conn) {
int index = pool.indexOf(conn);
connectionStates.set(index, 0);
}
}

public class DBClientThread extends Thread {
private DBConnection conn;
public DBClientThread(DBConnection conn) {
this.conn = conn;
}
public void run() {
System.out.println("Use DBConnection id: " + this.conn.getId());
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
DBConnectionFactory.releaseDBConnection(this.conn);
this.conn = null;
}
}

public class Test {
public static void main(String[] args) {
DBConnectionFactory.init(5);

for (int i = 0; i < 10; i++) {
DBConnection conn = DBConnectionFactory.getDBConnection();
if (conn != null) {
DBClientThread thread = new DBClientThread(conn);
thread.start();
} else {
System.out.println("Can not get a db conn.");
}

try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

输出如下:

1
2
3
4
5
6
7
8
9
10
Use DBConnection id: 0
Use DBConnection id: 1
Use DBConnection id: 2
Use DBConnection id: 3
Use DBConnection id: 4
Use DBConnection id: 0
Use DBConnection id: 1
Use DBConnection id: 2
Use DBConnection id: 3
Use DBConnection id: 4

上面的代码模拟了数据库连接池的使用,我们预先创建5个数据库连接,然后创建出10个线程去获取这个连接。当要获取连接时,不是去实时地创建连接,而是从连接池中获取,使用完连接后还要将连接放回池中(其实就是改变连接的状态)。这几个连接对象被共享出来,供各个线程获取。这是享元模式的一种很重要的应用。

优点

享元模式预先创建出一些对象,然后缓存这些对象,当需要时直接取出使用。可以帮助系统减少对象的创建,降低内存占用,提高系统性能。

缺点

享元模式会使系统的复杂度提高,我们需要维护这些对象的创建和存在。而且如果系统不需要对象时,这些预先创建的对象还会存在,也是占用内存的,不过这个一般不会成为太大问题。