先转载一段:http://www.developer.com/java/ent/article.php/626171
Suppose you have been given the assignment of writing a class library to provide access to a proprietary database. Clients will send queries to the database through a network connection. The database server will receive queries through the network connection and return the results through the same connection.
In order for a program to query the database, it must have a connection to the database. The most convenient way for programmers who will use the library to manage connections is for each part of a program that needs a connection to create its own connection. However, creating database connections that are not needed is bad for a few reasons:
Your design for the library will have to reconcile these conflicting forces. The need to provide a convenient API for programmers pulls your design in one direction. The high expense of creating database connection objects and a possible limit to the number of database connections that can exist at one time pulls your design in another direction. One way to reconcile these forces is to have the library manage database connections on behalf of the application that uses the library.
The strategy the library will use to manage database connections will be based on the premise that a program's database connections are interchangeable. So long as a database connection is in a state that allows it to convey a query to the database, it does not matter which of a program's database connections is used. Using that observation, the database access library will be designed to have a two-layer implementation of database connections.
A class called Connection will implement the upper layer. Programs that use the database access library will directly create and use Connection objects. Connection objects will identify a database but will not directly encapsulate a database connection. Only while a Connection object is being used to send a query to a database and fetch the result will it be paired with a ConnectionImpl object. ConnectionImpl objects encapsulate an actual database connection.
The database access library will create and manage ConnectionImpl objects. It will manage these objects by maintaining a pool of them that are not currently paired up with a Connection object. The library will only create a ConnectionImpl object when it needs to pair one up with a Connection object and the pool of ConnectionImpl objects is empty. The class diagram below shows the classes that will be involved in managing the pool of ConnectionImpl objects.
Figure 1
A Connection object calls the ConnectionPool object's AcquireImpl method when it needs a ConnectionImpl object, passing it the name of the database it needs to be connected with. If any ConnectionImpl objects in the ConnectionPool object's collection are connected to the needed database, the ConnectionPool object returns one of those objects. If there are no such ConnectionImpl objects in the ConnectionPool object's collection, it tries to create one and return it. If it is unable to create a ConnectionImpl object, then it waits until an existing ConnectionImpl object is returned to the pool by a call to the releaseImpl method and then it returns that object.
The ConnectionPool class is a singleton. There should only be one instance of the ConnectionPool class. The class's constructor is private. Other classes access the one instance of the ConnectionPool class by calling its getInstance method, which is static.
There are many reasons a ConnectionPool object's AcquireImpl method may be unable to create a ConnectionImpl object. Among these may be to impose a limit on the number of ConnectionImpl objects it may create that connect to the same database. The reason for the limit is to be able to guarantee that a database will be able to support a minimum number of clients. Since there will be a maximum number of connections each database can support, limiting the number of connections each client can have to a database allows you to guarantee support for a minimum number of client programs.
The general idea for the Connection Pool pattern is that if instances of a class can be reused, you avoid creating instances of the class by reusing them. The class diagram shows the roles that classes play in the Object Pool pattern: The diagram shows the general case for the Object Pool pattern.
Figure 2
Below are descriptions of the roles that classes that participate in the Object Pool pattern play in the above diagram:
Reusable
Instances of classes in this role collaborate with other objects for a limited amount of time, then they are no longer needed for that collaboration.
Client
Instances of classes in this role use Reusable objects.
ReusablePool
Instances of classes in this role manage Reusable objects for use by Client objects. Usually, it is desirable to keep all Reusable objects that are not currently in use in the same object pool so that they can be managed by one coherent policy. To achieve this, the ReusablePool class is designed to be a singleton class. Its constructor(s) are private, which forces other classes to call its getInstance method to get the one instance of the ReusablePool class.
A Client object calls a ReusablePool object's acquireReusable method when it needs a Reusable object. A ReusablePool object maintains a collection of Reusable objects. It uses the collection of Reusable objects to contain a pool of Reusable objects that are not currently in use. If there are any Reusable objects in the pool when the acquireReusable method is called, it removes a Reusable object from the pool and returns it. If the pool is empty, then the acquireReusable method creates a Reusable object if it can. If the acquireReusable method cannot create a new Reusable object, then it waits until a Reusable object is returned to the collection.
Client objects pass a Reusable object to a ReusablePool object's releaseReusable method when they are finished with the object. The releaseReusable method returns a Reusable object to the pool of Reusable objects that are not in use.
In many applications of the Object Pool pattern, there are reasons for limiting the total number of Reusable objects that may exist. In such cases, the ReusablePool object that creates Reusable objects is responsible for not creating more than a specified maximum number of Reusable objects. If ReusablePool objects are responsible for limiting the number of objects they will create, then the ReusablePool class will have a method for specifying the maximum number of objects to be created. That method is indicated in the above diagram as setMaxPoolSize.
In many cases, the object that manages an object pool is supposed to limit the number of instances of a class that can be created. It is easy for an object to limit the number of objects it creates. However, to robustly enforce a limit on the total number of objects created, the object responsible for managing the object pool must be the only object able to create those objects.
You can ensure that a class is instantiated only by the class that manages the object pool. You can do this by making the managed class's constructor(s) private and implementing the pool management class as a static member class of the managed class. If you do not have control over the structure of the class whose instances are to be managed, you may be able to add that structure to the class through inheritance.
Below is a code example that implements the design presented previously. The first listing shows part of the Connection class. The Connection class uses ConnectionImpl objects, which are managed in pools by the ConnectionImpl.ConnectionPool class.
Listing 1. The Connection class.
public class Connection {
private final static ConnectionImpl.ConnectionPool
connectionPool = ConnectionImpl.ConnectionPool.getInstance();
private String databaseName;
...
/**
* Send a request to the database and return the result.
*/
Object sendRequest(Request request) {
Object result;
ConnectionImpl impl = connectionPool.acquireImpl(databaseName);
result = impl.sendRequest(request);
connectionPool.releaseImpl(impl);
return result;
} // sendRequest(Request)
} // class Connection
The other listing presented here is of the ConnectionImpl class.
Listing 2. The ConnectionImpl class.
class ConnectionImpl {
// Name of the datbase this object connected to.
private String dbName;
// Private Constructor
private ConnectionImpl(String dbName) {
this.dbName = dbName;
...
} // constructor()
...
/**
* return the name of the database that this objects is connected to.
*/
String getDatabaseName() {
return dbName;
} // getDatabaseName()
/**
* Send a request to the database and return the result.
*/
Object sendRequest(Request request) {
Object result = null;
...
return result;
} // sendRequest(Request)
The rest of the ConnectionImpl class is the part that is most interesting with respect to the Object Pool pattern. It is the ConnectionImpl.ConnectionPool class, which manages a pool of ConnectionImpl objects. It is implemented as a static member of the ConnectionImpl class. Because it is a member of the ConnectionImpl class, it is allowed to access the ConnectionImpl class's constructor.
Listing 3. The ConnectionImpl.ConnectionPool class.
static class ConnectionPool {
// The one instance of this class
private static ConnectionPool thePool = new ConnectionPool();
// This hash table is associates database names with the a
// corresponding Vector that contains a pool of connections for
// that database.
private Hashtable poolDictionary = new Hashtable();
// This constructor is private to prevent other classes from
// creating instances of this class.
private ConnectionPool() {}
/**
* Return the one instance of this class.
*/
public static ConnectionPool getInstance() {
return thePool;
} // getInstance()
/**
* Return a ConnectionImpl from the apropriate pool or create one
* if the pool is empty.
* @param dbName The name of the database that a ConnectionImpl
* is to be supplied for.
*/
public synchronized ConnectionImpl acquireImpl(String dbName) {
Vector pool = (Vector)poolDictionary.get(dbName);
if (pool != null) {
int size = pool.size();
if (size > 0)
return (ConnectionImpl)pool.remove(size-1);
} // if null
// No ConnectionImpl in pool, so create one.
return new ConnectionImpl(dbName);
} // acquireImpl(String)
/**
* Add a ConnectionImpl to the appropriate pool.
*/
public synchronized void releaseImpl(ConnectionImpl impl) {
String databaseName = impl.getDatabaseName();
Vector pool = (Vector)poolDictionary.get(databaseName);
if (pool == null) {
pool = new Vector();
poolDictionary.put(databaseName, pool);
} // if null
pool.addElement(impl);
} // releaseImpl(ConnectionImpl)
} // class ConnectionPool
} // class ConnectionImpl
About the author
Mark Grand is the author of a series of books titled Patterns in Java. He is currently the chief architect of an application framework for e-commerce called EBox. Mark Grand can be contacted at [email protected].