`
waveeee
  • 浏览: 52512 次
  • 来自: 上海
社区版块
存档分类
最新评论

key generator

阅读更多
http://hi.baidu.com/jonsonda/blog/item/e5a0a712551fb5cdc2fd78f3.html

序列键生成器与单例及多例模式
2007-10-25 02:07
在一个关系数据库中,所有的数据都是存储在表里,而每一个表都有一个主键(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;
      }

      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 = '"
              + keyName + "'";
          String sql2 = "SELECT keyValue FROM KeyTable WHERE KeyName = '"
              + keyName + "'";
          // 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;
      }
}

从上面的代码可以看出,每当getNextKey()被调用时,这个方法都会根据缓冲区的大小和已经用过的键值来判断是否需要更新缓冲区。当缓冲区被更新后,KeyInfo会持有已经向数据库预定过的20个序列号码,并不断向调用者顺序提供这20个号码。等这20个序列号用完之后,KeyInfo对象就会向数据库预定后20个新号码。
当然,如果系统被重新启动,而缓冲区中的号码并没有用完的话,这些没有完的号码就不会再次被使用了。系统重新启动之后,KeyInfo对象会重新向数据库预定下面的20个号码,并向外界提供这20个号码。

package com.javapatterns.keygen.ver4;

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("PO_NUMBER"));
          }
      }
}

方案五:应用多例模式的设计方案

为了能够处理多系列键值的情况,除了可以将单例模式所封装的单一状态改为聚集状态之外,还可以采用多例模式。

package com.javapatterns.keygen.ver5;

import java.util.HashMap;

public class KeyGenerator {
      private static HashMap kengens = new HashMap(10);
      private static final int POOL_SIZE = 20;
      private KeyInfo keyinfo;

      private KeyGenerator() {}

      private KeyGenerator(String keyName) {
          keyinfo = new KeyInfo(POOL_SIZE, keyName);
      }

      public static synchronized KeyGenerator
              getInstance(String keyName) {
          KeyGenerator keygen;
          if (kengens.containsKey(keyName)) {
              keygen = (KeyGenerator) kengens.get(keyName);
          } else {
              keygen = new KeyGenerator(keyName);
              //以下语句为初晨之阳所加,原书中没有,疑似作者笔误
              keygens.put(keyName, keygen);
          }
          return keygen;
      }

      public int getNextKey() {
          return keyinfo.getNextKey();
      }
}

KeyInfo类的代码与方案四类似,不再重复。

package com.javapatterns.keygen.ver5;

public class Client {
      private static KeyGenerator keygen;

      public static void main(String[] args) {
          keygen = KeyGenerator.getInstance("PO_NUMBER");
          for (int i = 0 ; i < 25 ; i++) {
               System.out.println("key(" + (i+1)
                  + ")= " + keygen.getNextKey());
          }
      }
}

在上面给出的方案中,第四个和第五个方案都是具有实用价值的设计方案。

如果一个单例模式是一个聚集对象的话,那么这个聚集中所保存的是对其他对象的引用。一个多例模式则不同,多例对象使用一个聚集对象登记和保存自身的实例。由于这两种设计模式的相似之处,在很多情况下它们可以互换使用。
分享到:
评论

相关推荐

    Nero 14 Key Generator

    A key generator for Nero 14.

    The bat! key generator

    Key generator for The Bat! version 4.2, and has been tried with version 4.2-10.

    sap key generator1.0

    开发员想要修改SAP的object时(比如说SAP 程序)时,需要注册此Key,此key与SAP object相关。也就是说,对于一个object,你就需要一个key。看来我要进的这个Table是系统标准的程序中用的,所以系统要你输入这个key。

    FLEXlm Vendor Key Generator 3.0

    破解软件时常用到FLEXM,必须有VENDOR,通过此软件产生VENDOR,进一步破解

    vmware 9 key generator

    "vmware 9 key generator" 指的是为 VMware Workstation 9 生成序列号(key)的工具,通常这类工具被称为激活码生成器或者密钥生成器。在本文中,我们将深入探讨 VMware Workstation 9 的功能、虚拟化技术以及关于...

    ER Studio7.0 Key Generator

    标题中的"ER Studio 7.0 Key Generator"指的是该软件的一个版本——第七版的密钥生成器,这通常是为了激活软件的全功能而使用的。 ER设计是数据库设计过程中的关键步骤,它帮助开发者理解和可视化数据之间的关系。...

    Keygen+(Sap+R3+License+And+Object+Key+Generator+v1[1].70)\KEYGEN简单说明.doc

    Keygen+(Sap+R3+License+And+Object+Key+Generator+v1[1].70)\KEYGEN简单说明.doc

    IAR Key Generator

    IAR Key generator, It will generate IAR key number for 8051, ARM, AVR PIC, ATMEL,

    Sap R3 License Keys Generator

    Sap R3 License Keys Generator,

    Borland Product Key Generator

    then click Generate to generate the license key and slip. Click Save to save the license file. Note that you can't have more than one license for any product, if you generate new license for a product...

    SAP终极破解文件R/3系列

    Keygen (Sap R3 License And Object Key Generator v1.70) 知道的,我就不用多说了。很多人都因为有这个赚了不少钱了哦,有分的就下吧。收藏一下哦!

    密钥生成工具 puttygen,windows系统下用于生成公钥和私钥。

    Puttygen,全称为PuTTY Key Generator,是Windows操作系统下广泛使用的工具,专门用于生成SSH(Secure Shell)的公钥和私钥对。这个工具是PuTTY套件的一部分,PuTTY是一个流行的免费终端模拟器和网络文件传输应用...

    Labview 8.5.1 Key Generator

    Labview 8.5.1 keygen

    Free Key Generator-开源

    很小而又快速的密码生成器。

    S/Key Generator for J2ME architecture-开源

    本文将深入探讨一款名为"S/Key Generator"的开源项目,它是专门为Java Mobile Edition (J2ME) 架构设计的一次性密码(OTP)生成器。该工具遵循RFC2289标准,旨在为使用Java语言的手机提供额外的安全保障,确保用户数据...

    SAP算号器 license key Developer Access Key 完美解决方案

    SAP算号器 License Key 和 Developer Access Key 解决方案 SAP算号器 License Key 和 Developer Access Key 是 SAP 系统中重要的组件,它们扮演着关键的角色在 SAP 系统的安装、配置和维护中。然而,生成和获取這些...

    ABAP开发账号访问关键字(ACCESS_KEY)获取方法

    在进行ABAP开发的过程中,为了确保系统的安全性与稳定性,SAP系统通常会要求开发者拥有一个有效的访问关键字(ACCESS_KEY),也被称为开发账号访问关键字。本文将详细介绍如何获取这一重要的访问关键字,包括其背景...

    mma12_0_keygen.exe

    Mathematica Key Generator, 可以激活Mathematica 12软件, 此为exe版本。

    Infragistics NetAdvantage 2010.1 注册码Key

    在Infragistics NetAdvantage 2010.1的情况下,提供的注册码Key可以激活对应版本的软件,使其从试用版变为全功能版。 ### 注册码Key的具体使用方法 在使用这些注册码Key时,用户需要遵循一定的步骤来确保其能够...

Global site tag (gtag.js) - Google Analytics