Builder Design Pattern
Intent
Seperate the construction of a complex object from it representation so that the same construction process can create different representations.
Applicability
Use builder pattern when
- The algoritm for creating a complex object should be independent of the parts that make up the object and how they are assembled.
- The construction process must allow different representations for the object that is constructed.
- More general usage is when your class constructor requires too many parameters.(Recommended by Effective Java by Josh Bloch)
UML diagram
Participants
- Builder
- Defines an abstract interface for creating parts of the product.
- Concrete Builder
- Defines interface for retrieving the product.
- Builds the products internal representation and defines the process by which it is assembled.
- Director
- Constructs the product using the builder interface
- Product
- Represents the complex object under construction.
Coonsequences
- Lets you vary a products internal representation
- Isolate code for construction and representation
- It gives finer control over the construction process
Implementation
Here are the implementation issues to consider
- Builder interface must be general enough to allow all kinds of concrete builders for construction of products.
- The Builder methods are defined as empty methods instead of declaring them as abstract(pure virtual in C++). This is to let the client override only the operations they are interested in.
Example Code
Below is the code for implementing Connection Pool. Here we used builder to avoid contructor with more parameters as specified in Usege #3. In this case there is only one Builder so we have created inner class inside the ConnectionPool class. In case of other usages there can exist more than one representation of the product and each representation is constructed by different Builder. And we construct Director using the appropriate Builder object.
Connection.java
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
package com.practice.connectionpool.pool;
public class Connection {
public static Integer count = 0;
private String userName;
private String password;
private String connectionUrl;
private Integer id;
public Connection(String userName, String password, String connectionUrl){
this.userName = userName;
this.password = password;
this.connectionUrl = connectionUrl;
count++;
this.id = count;
}
public static Integer getCount() {
return count;
}
public static void setCount(Integer count) {
Connection.count = count;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getConnectionUrl() {
return connectionUrl;
}
public void setConnectionUrl(String connectionUrl) {
this.connectionUrl = connectionUrl;
}
}
IConnectionPool.java
1
2
3
4
5
6
package com.practice.connectionpool.pool;
public interface IConnectionPool {
public Connection getConnection();
public void releaseConnection(Connection con);
}
ConnectionPool.java
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package com.practice.connectionpool.pool;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConnectionPool implements IConnectionPool {
private String userName;
private String password;
private String connectionUrl;
private Integer maxSize;
private Integer minSize;
List<Connection> availableConnections;
Map<Connection, Connection> usedConnections;
Lock lock;
public ConnectionPool() {
lock = new ReentrantLock();
availableConnections = new ArrayList<>();
usedConnections = new HashMap<>();
}
protected void init() {
/* Create predefined number of connections */
for(int i=0; i < minSize; i++) {
availableConnections.add(new Connection(userName,password,connectionUrl));
}
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
lock.lock();
this.userName = userName;
lock.unlock();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
lock.lock();
this.password = password;
lock.unlock();
}
public String getConnectionUrl() {
return connectionUrl;
}
public void setConnectionUrl(String connectionUrl) {
lock.lock();
this.connectionUrl = connectionUrl;
lock.unlock();
}
public List<Connection> getAvailableConnections() {
return availableConnections;
}
public void setAvailableConnections(List<Connection> availableConnections) {
lock.lock();
this.availableConnections = availableConnections;
lock.unlock();
}
public Integer getMaxSize() {
return maxSize;
}
public void setMaxSize(Integer maxSize) {
lock.lock();
this.maxSize = maxSize;
lock.unlock();
}
public Integer getMinSize() {
return minSize;
}
public void setMinSize(Integer minSize) {
lock.lock();
this.minSize = minSize;
lock.unlock();
}
public Map<Connection, Connection> getUsedConnections() {
return usedConnections;
}
public void setUsedConnections(Map<Connection, Connection> usedConnections) {
lock.lock();
this.usedConnections = usedConnections;
lock.unlock();
}
@Override
public Connection getConnection() {
Connection con = null;
//We have already used maximum number of connections
if(getUsedConnections().size() >= getMaxSize())
return null;
lock.lock();
if(getAvailableConnections().size() !=0 ) {
con = getAvailableConnections().remove(0);
getUsedConnections().put(con,con);
}
else {
con = new Connection(userName,password,connectionUrl);
getUsedConnections().put(con,con);
}
lock.unlock();
return con;
}
@Override
public void releaseConnection(Connection con) {
lock.lock();
getAvailableConnections().add(getUsedConnections().remove(con));
lock.unlock();
System.out.println("Released connection : " + con.getId());
}
public static class Builder{
private String userName;
private String password;
private String connectionUrl;
private Integer maxSize;
private Integer minSize;
public String getUserName() {
return userName;
}
public Builder setUserName(String userName) {
this.userName = userName;
return this;
}
public String getPassword() {
return password;
}
public Builder setPassword(String password) {
this.password = password;
return this;
}
public String getConnectionUrl() {
return connectionUrl;
}
public Builder setConnectionUrl(String connectionUrl) {
this.connectionUrl = connectionUrl;
return this;
}
public Integer getMaxSize() {
return maxSize;
}
public Builder setMaxSize(Integer maxSize) {
this.maxSize = maxSize;
return this;
}
public Integer getMinSize() {
return minSize;
}
public Builder setMinSize(Integer minSize) {
this.minSize = minSize;
return this;
}
public ConnectionPool build() {
ConnectionPool pool = new ConnectionPool();
pool.setUserName(userName);
pool.setPassword(password);
pool.setConnectionUrl(connectionUrl);
if(maxSize != 0)
pool.setMaxSize(maxSize);
else
pool.setMaxSize(5);
if(minSize != 0)
pool.setMinSize(minSize);
else
pool.setMinSize(3);
pool.init();
return pool;
}
}
}
ConnectionManager.java
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
package com.practice.connectionpool.manager;
import com.practice.connectionpool.pool.Connection;
import com.practice.connectionpool.pool.ConnectionPool;
import com.practice.connectionpool.pool.IConnectionPool;
public class ConnectionManager {
private static ConnectionManager manager = null;
IConnectionPool pool = null;
private ConnectionManager(){
init();
}
public static ConnectionManager getConnectionManager() {
if(manager == null) {
synchronized(ConnectionManager.class) {
if(manager == null)
manager = new ConnectionManager();
}
}
return manager;
}
public void init(){
pool = new ConnectionPool.Builder()
.setUserName("dsama")
.setPassword("password")
.setConnectionUrl("")
.setMaxSize(10)
.setMinSize(2)
.build();
}
public Connection getConnetion() {
return pool.getConnection();
}
public void releaseConnection(Connection con) {
pool.releaseConnection(con);
}
}
ConnectionPoolTest.java
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
package com.practice.connectionpool.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.practice.connectionpool.manager.ConnectionManager;
import com.practice.connectionpool.pool.Connection;
public class ConnectionPoolTest implements Runnable {
public void run(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ConnectionManager manager = ConnectionManager.getConnectionManager();
Connection connection = manager.getConnetion();
System.out.println("Thread id : " + Thread.currentThread().getId() + "Connection id : " + connection.getId());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
manager.releaseConnection(connection);
}
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(6);
for(int i=0;i < 20; i++) {
executor.submit(new ConnectionPoolTest());
}
executor.awaitTermination(1, TimeUnit.DAYS);
executor.shutdown();
}
}