一、享元模式的核心思想–共享
享元的英文是 Flyweight,它是一个来自于体育方面的专业用语,在拳击、摔跤和举重比赛中特指最轻量的级别。把这个单词移植到软件工程里面,也是用来表示特别小的对象,即细粒度对象。Flyweight享元,可以理解为共享元对象,也就是共享细粒度对象。享元模式就是通过使用共享的方式,达到高效地支持大量的细粒度对象。它的目的就是节省占用的空间资源,从而实现系统性能得到改善。
享元模式主要作用是实现对象的共享,即使用共享池,从而减少内存分配的开销。享元模式通常与工厂模式一起使用,它包含了多个共享的组合对象,因此:享元模式=单例模式+工厂模式+合成模式,其结构如图下所示。
其中:FlyweightFactoiy享元工厂负责创建和管理享元对象,它必须保证享元对象可以被系统适当共享。当一个客户端对象请求一个享元对象的时候,享元工厂需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂就应当创建一个新的合适的享元对象。Flyweight是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。MyFlyweight1和MyFlyweight2 则是实现抽象享元所规定的接口。
二、何时使用享元模式
在程序设计中会出现这样的情况:看起来似乎需要大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本都是相同的,有时就能够大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。如果从类中移出某些数据并存储到外部,且这样能大幅度地减少程序需要保存的不同类实例的数量,就适合使用享元模式,以避免大量非常相似的类的开销。
享元模式只是用几个对象实例表示程序中的多个不同对象,这些实例通常把相同的基本属性作为内部数据,把少数几个属性作为外部数据,它们通常随类实例表现形式不同而不同。
三、Java 中的应用–数据库连接池
享元模式由于其共享特性,可以使用在任何池化的操作中,如线程池、数据库连接池等。数据库连接池是享元模式的一个典型应用。在该应用中,需要一个连接池工厂类ConnectPool,它是多个连接 Connection 的聚集,其结构如下图所示。
连接池工厂类 ConnectPool具有一个数据集合对象 pool,它在构造函数中进行初始化。该类提供了一个单例的工厂类,以防止重复创建该工厂实例,并提供取得连接getConnection()和释放连接freeConnection()函数来分别从共享池中取得和释放一个连接。
其源代码如下程序所示。
package structure.flyweight;
import java.sql.Connection;
import java.sql.SOLException;
import java.util.Veetor;
/**
* @author Minggg
* 享元模式-连接池实例
*/
public class ConnectionPool {
private Vector<Connection> pool;
private String url = "jdbc:mysql://localhost:3306/test";
private String username = "root" ;
private String password = "123456";
private String driverClassName = "com.mysql.jdbc.Driver";
//连接池的大小,也就是连接池中有多少个数据库连接
private int poolSize = 100;
private static ConnectionPool instance = null;
/**
* 私有的构造方法,禁止外部创建本类的对象,要想获得本类的对象,通过<code>getlstale</code>方
* 使用了设计模式中的单子模式
*/
private ConnectionPool() {
pool = new Vector<Connection>(poolSize);
// 在连接池中创建初始设置的数据库连接
Connection conn = null;
for (int i = 0; i< poolSize; i++){
try {
Class.forName(driverClassName);
conn = java.sql.DriverManager.getConnection(url, username, password);
pool.add(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace(;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 返回连接到连接池中
*/
public synchronized void release(Connection conn) {
pool.add(conn);
}
/**
* 关闭连接池中的所有数据库连接
*/
public synchronized void closePool() {
for (int i = 0; i < pool.size(); i++){
try {
((Connection) pool.get(i)).close();
} catch (SQLException e) {
e.printStackTrace();
}
pool.remove(i);
}
}
/**
* 返回当前连接池的一个对象
*/
public static ConnectionPool getInstanee() {
if (instance == null){
instance = new ConnectionPool();
}
return instance;
}
/**
* 返回连接池中的一个数据库连接
*/
public synchronized Connection getConnection(){
if(pool.size() > 0) {
Connection conn = pool.get(0);
pool.remove(conn);
return conn;
} else {
return null;
}
}
}
要使用该连接池,只需要创建一个连接池的实例pool,然后使用getConnection0来取得一个连接执行 SOL 查询,最后使用freeConnection()来释放连接即可。其源代码如下程序所示。
package structure.flyweigh;
import java.sal.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class ConnectionPoolTest {
public static void main(String[] args) throws Exception {
String sql = "select id,name,phone from guestmessage";
ConnectionPool pool= ConnectionPool.getInstance();
for (inti= 0; i< 100; i++){
Connection conn = pool.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()){}
rs.close();
stmt.close();
pool.release(eonn);
}
pool.closePool();
}
}
通过以上连接池的管理,实现了数据库连接的共享,不需要每一次取得连接时都重新创建,因为节省了重复创建数据库连接的开销,使得程序的性能大幅提升。