《Java与模式》学习笔记——序列键生成器与单例及多例模式
Posted brooksychen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java与模式》学习笔记——序列键生成器与单例及多例模式相关的知识,希望对你有一定的参考价值。
在一个关系数据库中,所有的数据都是存储在表里,而每一个表都有一个主键(Primary Key)。对大多数的用户输入数据来讲,主键需要由系统以序列号方式产生。比如一个餐馆的贩卖系统需要一个序列号给每天开出去的卖单编号,这个序列号码就应当存放到数据库里面。每当发出序列号码的时候,都应当从数据库读取这个号码,并更新这个号码。
为了保证在任何情况下键值都不会出现重复,应当使用预定式键值存储办法。在请求一个键值时,首先将数据库中的键值更新为下一个可用值,然后将旧值提供给客户端。这样万一出现运行中断的话,最多就是这个键值被浪费掉。
与此相对的是记录式键值存储办法。也就是说,键值首先被返还给客户端,然后记录到数据库中去。这样做缺点明显,因此不要使用这种登记式的存储办法。
预定式的存储办法可以每一次预定多个键值(也即一个键值区间),而不是每一次仅仅预定一个值。由于这些值都是一些序列数值,因此,所谓一次预定多个值,不过就是每次更新键值时将键值增加一个大于1的数目。
这个序列键管理器可以设计成一个单例类。
下面从一个最简单的情况出发,逐渐将问题的复杂性提高,直到给出具有实用价值的解决方案为止。
方案一:没有数据库的情况
package
com.javapatterns.keygen.ver1;
public class KeyGenerator ... {
private static KeyGenerator keygen = new KeyGenerator();
private int key = 1000;
private KeyGenerator() ...{}
public static KeyGenerator getInstance() ...{
return keygen;
}
public synchronized int getNextKey() ...{
return key++;
}
}
public class KeyGenerator ... {
private static KeyGenerator keygen = new KeyGenerator();
private int key = 1000;
private KeyGenerator() ...{}
public static KeyGenerator getInstance() ...{
return keygen;
}
public synchronized int getNextKey() ...{
return key++;
}
}
package
com.javapatterns.keygen.ver1;
public class Client ... {
private static KeyGenerator keygen;
public static void main(String[] args) ...{
keygen = KeyGenerator.getInstance();
System.out.println("key = " + keygen.getNextKey());
System.out.println("key = " + keygen.getNextKey());
System.out.println("key = " + keygen.getNextKey());
}
}
public class Client ... {
private static KeyGenerator keygen;
public static void main(String[] args) ...{
keygen = KeyGenerator.getInstance();
System.out.println("key = " + keygen.getNextKey());
System.out.println("key = " + keygen.getNextKey());
System.out.println("key = " + keygen.getNextKey());
}
}
这一设计基本上实现了向客户端提供键值的功能,但是也有明显的缺点。由于没有数据库的存储,一旦系统重新启动,KeyGenerator都会重新初始化,这就会造成键值的重复。为了避免这一点,就必须将每次的键值存储起来,以便一旦系统和重启时,可以将这个键值取出,并在这个值的基础上重新开始。
方案二:有数据库的情况
package
com.javapatterns.keygen.ver2;
public class KeyGenerator ... {
private static KeyGenerator keygen = new KeyGenerator();
private KeyGenerator() ...{}
public static KeyGenerator getInstance() ...{
return keygen;
}
public synchronized int getNextKey() ...{
return getNextKeyFromDB();
}
private int getNextKeyFromDB() ...{
String sql1 = "UPDATE KeyTable SET keyValue = keyValue + 1 ";
String sql2 = "SELECT keyValue FROM KeyTable";
//execute the update SQL
//run the SELECT query
//这里只是示意性地返回一个数值
return 1000;
}
}
public class KeyGenerator ... {
private static KeyGenerator keygen = new KeyGenerator();
private KeyGenerator() ...{}
public static KeyGenerator getInstance() ...{
return keygen;
}
public synchronized int getNextKey() ...{
return getNextKeyFromDB();
}
private int getNextKeyFromDB() ...{
String sql1 = "UPDATE KeyTable SET keyValue = keyValue + 1 ";
String sql2 = "SELECT keyValue FROM KeyTable";
//execute the update SQL
//run the SELECT query
//这里只是示意性地返回一个数值
return 1000;
}
}
Client类的代码与方案一类似,不再重复。
在接到客户端的请求时,这个KeyGenerator每次都向数据库查询键值,将新的键值登记到表里,然后将查询的结果返还给客户端。上面的代码中,只给出了SQL语句,为了将注意力集中在系统设计上而并没有给出执行这两行语句的JDBC代码。
方案三:键值的缓存方案
每一次都进行键值的查询,有必要吗?毕竟一个键的值只是一些序列号码,与其每接到一次请求就查询一次,然后向客户端提供这一个值,不如在一次查询中一次性地预先登记多个键值,然后连续多次地向客户端提供这些预订的键值。这样一来,不是节省了大部分不必要的数据库查询操作吗?
这就是键值的缓存机制。当KeyGenerator每次更新数据库中的键值时,它都将键值增加。与方案二不同之处是,键值的增加值不是1而是更多。下面的例子中,键值的增加值是20.为了存储所有的与键有关的信息,特地引进一个KeyInfo类,这个类除了存储与键有关的信息外,还提供了一个retrieveFromDB()方法,向数据库查询键值。
package
com.javapatterns.keygen.ver3;
public class KeyGenerator ... {
private static KeyGenerator keygen = new KeyGenerator();
private static final int POOL_SIZE = 20;
private KeyInfo key ;
private KeyGenerator() ...{
key = new KeyInfo(POOL_SIZE);
}
public static KeyGenerator getInstance() ...{
return keygen;
}
public int getNextKey() ...{
return key.getNextKey();
}
}
public class KeyGenerator ... {
private static KeyGenerator keygen = new KeyGenerator();
private static final int POOL_SIZE = 20;
private KeyInfo key ;
private KeyGenerator() ...{
key = new KeyInfo(POOL_SIZE);
}
public static KeyGenerator getInstance() ...{
return keygen;
}
public int getNextKey() ...{
return key.getNextKey();
}
}
package
com.javapatterns.keygen.ver3;
class KeyInfo ... {
private int keyMax;
private int keyMin;
private int nextKey;
private int poolSize;
public KeyInfo(int poolSize) ...{
this.poolSize = poolSize;
retrieveFromDB();
}
public int getKeyMax() ...{
return keyMax;
}
public int getKeyMin() ...{
return keyMin;
}
public synchronized int getNextKey() ...{
if (nextKey > keyMax) ...{
retrieveFromDB();
}
return nextKey++;
}
private void retrieveFromDB() ...{
String sql1 = "UPDATE KeyTable SET keyValue = keyValue + "
+ poolSize + " WHERE keyName = 'PO_NUMBER'";
String sql2 = "SELECT keyValue FROM KeyTable WHERE KeyName = 'PO_NUMBER'";
// execute the above queries in a transaction and commit it
// assume the value returned is 1000
int keyFromDB = 1000;
keyMax = keyFromDB;
keyMin = keyFromDB - poolSize + 1;
nextKey = keyMin;
}
}
class KeyInfo ... {
private int keyMax;
private int keyMin;
private int nextKey;
private int poolSize;
public KeyInfo(int poolSize) ...{
this.poolSize = poolSize;
retrieveFromDB();
}
public int getKeyMax() ...{
return keyMax;
}
public int getKeyMin() ...{
return keyMin;
}
public synchronized int getNextKey() ...{
if (nextKey > keyMax) ...{
retrieveFromDB();
}
return nextKey++;
}
private void retrieveFromDB() ...{
String sql1 = "UPDATE KeyTable SET keyValue = keyValue + "
+ poolSize + " WHERE keyName = 'PO_NUMBER'";
String sql2 = "SELECT keyValue FROM KeyTable WHERE KeyName = 'PO_NUMBER'";
// execute the above queries in a transaction and commit it
// assume the value returned is 1000
int keyFromDB = 1000;
keyMax = keyFromDB;
keyMin = keyFromDB - poolSize + 1;
nextKey = keyMin;
}
}
package
com.javapatterns.keygen.ver3;
public class Client ... {
private static KeyGenerator keygen;
public static void main(String[] args) ...{
keygen = KeyGenerator.getInstance();
for (int i = 0 ; i < 25 ; i++) ...{
System.out.println("key(" + (i+1)
+ ")= " + keygen.getNextKey());
}
}
}
public class Client ... {
private static KeyGenerator keygen;
public static void main(String[] args) ...{
keygen = KeyGenerator.getInstance();
for (int i = 0 ; i < 25 ; i++) ...{
System.out.println("key(" + (i+1)
+ ")= " + keygen.getNextKey());
}
}
}
现在,这个键值生成器已经具有如下的功能:在整个系统是唯一的,能将生成的键值存储到数据库中,以便在系统重新启动时也能够继续键值的生成,而不会造成键值上的重复。
这本来已经足够好了,但是还有一点值得设计师考虑改进的是,一般的系统都不会只有一个键值,而是有多个键值需要生成。怎么让上面的设计适用于任意多个键值的情况呢?
首先,由于KeyGenerator是单例类,因此,给出多个KeyGenerator的实例并无可能,除非将之推广为多例类。
其次,虽然KeyGenerator是单例类,但KeyGenerator仍然可以在内部使用一个聚集管理多个键值。换言之,可以使用一个本身是单例对象的聚集对象,配合上合适的接口达到目的。
方案四:有缓存的多序列键生成器
此方案是对方案三的改进,引进了一个聚集来存储不同序列键信息的KeyInfo对象。
package
com.javapatterns.keygen.ver4;
import java.util.HashMap;
public class KeyGenerator ... {
private static KeyGenerator keygen = new KeyGenerator();
private static final int POOL_SIZE = 20;
private HashMap keyList = new HashMap(10);
private KeyGenerator() ...{}
public static KeyGenerator getInstance() ...{
return keygen;
}
public int getNextKey(String keyName) ...{
KeyInfo keyinfo;
if ( keyList.containsKey(keyName) ) ...{
keyinfo = (KeyInfo) keyList.get(keyName);
System.out.println("key found");
} else ...{
keyinfo = new KeyInfo(POOL_SIZE, keyName);
keyList.put(keyName, keyinfo);
System.out.println("new key created");
}
return keyinfo.getNextKey();
}
}
import java.util.HashMap;
public class KeyGenerator ... {
private static KeyGenerator keygen = new KeyGenerator();
private static final int POOL_SIZE = 20;
private HashMap keyList = new HashMap(10);
private KeyGenerator() ...{}
public static KeyGenerator getInstance() ...{
return keygen;
}
public int getNextKey(String keyName) ...{
KeyInfo keyinfo;
if ( keyList.containsKey(keyName) ) ...{
keyinfo = (KeyInfo) keyList.get(keyName);
System.out.println("key found");
} else ...{
keyinfo = new KeyInfo(POOL_SIZE, keyName);
keyList.put(keyName, keyinfo);
System.out.println("new key created");
}
return keyinfo.getNextKey();
}
}
package
com.javapatterns.keygen.ver4;
class KeyInfo ... {
private int keyMax;
private int keyMin;
private int nextKey;
private intJava Object 序列化与单例模式 [ 转载 ]
class KeyInfo ... {
private int keyMax;
private int keyMin;
private int nextKey;
private intJava Object 序列化与单例模式 [ 转载 ]