《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++;
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());
这一设计基本上实现了向客户端提供键值的功能,但是也有明显的缺点。由于没有数据库的存储,一旦系统重新启动,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;
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();
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;
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());
现在,这个键值生成器已经具有如下的功能:在整个系统是唯一的,能将生成的键值存储到数据库中,以便在系统重新启动时也能够继续键值的生成,而不会造成键值上的重复。 这本来已经足够好了,但是还有一点值得设计师考虑改进的是,一般的系统都不会只有一个键值,而是有多个键值需要生成。怎么让上面的设计适用于任意多个键值的情况呢? 首先,由于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();
package com.javapatterns.keygen.ver4;
class KeyInfo ...
private int keyMax;
private int keyMin;
private int nextKey;
private int poolSize;
private String keyName;
public KeyInfo(int poolSize, String keyName) ...
this.poolSize = poolSize;
this.keyName = keyName;
retrieveFromDB();
public int getKeyMax() ...
return keyMax;
Java Object 序列化与单例模式 [ 转载 ]