银行业务调度系统_改进 与 模板方法设计模式 共享设计模式

一、功能需求:

1、银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。

2、有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。

3、异步随机生成各种类型的客户,生成各类型用户的概率比例为:

VIP客户:普通客户:快速客户 = 163

4、客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需

时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。

5、各类型客户在其对应窗口按顺序依次办理业务。

6、当VIP6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等

待办理业务的时候,则优先处理对应客户的业务。

7、随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。

8、不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。

二、面试对象设计:

从功能需求提取关键信息:

名词分析法:

1.窗口:三种 普通窗口、快速窗口、VIP窗口是否需要创建三种不同类(看具体需求,若三者没有要求的数据或独立拥有的数据,或者是三者之间的功能提供的方法都是一致的,没有差异或只有微小的差异且这种关系是不具有扩展性,没有别一个XXX窗口以后来进来。笔者认为可以把三者当成一个类来看待。)

2.客户:有三种客户,但需求中并没有告诉客户需要的其它属性,比如客户手机电话、家庭或办公地址电话之类的,都没有要求。且这个客户具有什么功能呢,似乎也分析不出来,客户去银行是要银行提供服务着。所以客户只是个消费者,但客户又没有需求数据和提供方法的,所以这里只需要将客户虚拟当成字符串打印出来即可。

3. 业务:似乎是个类但这里业务只是被处理且处理它时,花费了某些时间。这是窗口进行的处理,业务本身不有要求业务被处理的细节,比如什么类型的业务,业务是需要查看相关信息:如上月水电费度数 本月水电费度数 每度多少单价 等等信息。而仅仅是哪个类型客户业务被处理。所以也不需要一个对象类,只需要 哪个客户业务正在处理,什么时候处理完了。

经第一步处理,可以得出暂时有以下类需要讨论

1.窗口:是否三个窗口待讨论。嗯,从需求分析出,三个窗口都是外面提供为客户服务着,且服务时需要找到对象的客户。这里引用另一个内容,如何去找对应的客户服务。是有个控制器将客户new出来,扔给窗口?不对,窗口应该是主动通过一个取号机,来看是否有产生了号码了。有号码在排队了,则叫相应排队号码的客户进来被窗口服务。因三种客户窗口,是根据客户类型不同处理业务时间不同和处理对象细节不同如快速和vip空闲时,可以处理普通。所以肯定需要三个类,每个类中具体的处理对象要到的号方式途径不同。必须需要从三个类的基类窗口中得到此抽象方法再实现此方法细节。可将取到号后的,服务暂停动作和没取到号休息功能等都是一样的,在基本中声明此方法,并用抽象方法获得号码。 这明显可以用方法模板模式来搞定嘛。

2. 嗯,提供讨论出需要一个取号机。但深入去分析时,取号机取的号是否是一样的呢。是否所有客户取出来的号都必须一样或唯一或叫产生出的顺序码必须是唯一,还是可以重复?同一类型客户唯一,还是所有类型客户统一一起唯一呢。显然是需要三个不同的号码管理器(有三种不同号码呢)来各自管理产生的号码。

3. 又引申出来一个类,号码管理器对。取号码机拥有号码管理器,两者关系应该是组合的。号码管理器不应该离开取号机,而取号机存在时必须拥有号码管理器。所以这里的应该是取号机,拥有三个号码管理器。实现上,还有更优先的设计,现在是定了三种客户类型、三处窗体类型。可以为未来考虑扩展,下次说不定有什么新的客户类型与窗口出来了,所以更优方案,应该是通过配置文件来配置,N个号码管理器应该有N种号码类型标识名(对客户隐藏具体号码类型标识名细节,客户只需要知道用哪个类型号标识找取号机要号即可)。 然后在构造始化时一次性从配置文件中读取共有哪N种号码类型标识名并找到后,分别给每一个类型标点名new 一个号码管理器并用对象池简单起见一个map,下步取号机将客户传过来的类型号标识,从一个对象池中找是否已经有对象了。有对象了,则用此对象去处理要号的动作。若没有对象,则报错程序停止,是否调用的代码传入一参数不对。

第二步处理时考虑优先,扩展改进:

1. 要考虑需求的扩展,必须优先考虑到,是否存在以后要加入新的客户窗口类型、新的客户,新的服务客户产生的时间不同,处理业务需要的随机数不同,为哪些类型客户处理规则不同? 很有可能会存在呢。所以固定式的一个类,类中多层判断与枚举固定客户类型,判断等都是不合适的。

2.取号机,客户与客户窗口应该都只需要找取号机取走号和叫一下号来服务,肯定不需要让外面知道有号码管理器的存在。所以号码管理器可以是取号机的私有内部类即可。为了考虑扩展性,取号机也不能简简单单只拥有三个号码管理器对象就可以了。因为到底有几种客户窗口类型与客户,是不确定的。所有需要有个号码管理器池对象,肯定外面需要的号码类型不对,用不同的号码管理器对象产生号码或取出号码。

三、画图

上面分析差不多了,可以画图看看,具体哪些类有什么方法。如下所图:

四、代码:

1. Window基类 AbstractBusinessWindow

package com.isoftstone.bankcontroller;

import java.util.Random;
import java.util.concurrent.Executors;

/**
 * 银行业务窗口,共有三种普通银行业务窗口、快速业务窗口、vip业务窗口。
 * 从本质来上,它们具体的功能主体都是一样的,叫号。找取号机叫出一个号,接着为此号码服务。
 * 只不过它们在具体处理对象不同且可能花费的时间也可能不同。
 * @author chen
 *
 */
public abstract class AbstractBusinessWindow {
	
	//每个窗口都有自己的名称
	private String name;
	
	/**
	 * 对外提供开始服务,服务打开时应该就可以一直自己在运行,于是必须要搞个线程出来
	 */
	public void service(){
		Executors.newSingleThreadExecutor().execute(
				new Runnable(){
					public void run(){
						while(true)
							startService();
					}
				}
		);
		
	}
	
	/**
	 * 每个窗口实例创建后,就应该去为相应的客户去服务。
	 * 即找取号机要相应的业务排队号码。
	 * 要到号码后,即为此号码的客户进行服务。
	 */
	private void startService(){
		
		System.out.println(name +" 服务准备就绪已经开始叫号"); 
		Integer num = serviceNumber(); //获得即将需要服务的排队号
		String customerTypeName = serviceCustomerTypeName();//由于快速窗口与vip窗口服务的客户可能是普通客户或自己的客户,所以不能将此方法放在获取号码之前
		if(null != num){  //取到的num,可能是自己的,也有可能是快速和vip为普通客户服务着
			System.out.println(name +",为"+customerTypeName+"__"+num+"服务中...");
			int handingTime = serviceNeedHandingTime();
			try {
				Thread.sleep(handingTime*1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(name +",为"+customerTypeName+"__"+num+"服务完成,共耗时"+handingTime+"秒");
		}else{ //没有取得号码,则休息,这里的没有取到号,对快速和vip来说既没有取得自己的快速或vip,也没有取得普通
			System.out.println(name +",没有取到"+customerTypeName+" 服务任务,先休息一秒种");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	/**
	 * 注意各个窗口需要处理的业务耗时,若不是在最小与最大的时间的,需要重写此方法
	 * @return 返回业务处理的时间,时间为秒数
	 */
	public  int serviceNeedHandingTime(){
		
		/*读取相应的配置信息中的最小与最大业务处理时间 
		 */
		int maxHandBussinessTime = Integer.valueOf(ConfigReader.getInstance().readConfig("max_handing_business_time"));
		int minHandBussinessTime = Integer.valueOf(ConfigReader.getInstance().readConfig("min_handing_business_time"));
		int maxRandNum = maxHandBussinessTime - minHandBussinessTime; 
		Random rd = new Random();
		//分别获取最大的业务处理时间与最小的业务处理时间
		int serviceTime = rd.nextInt(maxRandNum)+1+minHandBussinessTime; //获得大于minHandingTime,小于maxRandNum的随机时间
		return serviceTime;
	} 

	/**
	 * 为了打印方便,可看到窗口服务的是哪个客户名,所以需要返回一个客户类别名
	 * @return
	 */
	public abstract String serviceCustomerTypeName();
	
	/**
	 * 由于每个具体的业务窗口不同,所以各窗口找取号码机要号时要的过程方法也不同。所以应该由具体的子类来实现。
	 * 获得一个即将要服务的排队号码
	 * @return 返回一个即将要服务的排队号码
	 */
	public abstract Integer serviceNumber();
	

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}


2. CommonBusinessWindow 普通客户窗口类代码:

package com.isoftstone.bankcontroller;

/**
 * 普通客户窗口,只为普通客户服务。相对来说比较简单,不需要覆盖serviceNeedHandingTime方法,因为本身来说也是在最小与最大业务时间之内随机产生的
 * 剩下的只是,普通客户窗口服务的,客户类型名永远是“普通客户”
 * 获取的号码,也是找取号机要普通COMMON号码标识的号
 * @author chen
 *
 */
public class CommonBusinessWindow extends AbstractBusinessWindow {

	/**
	 * 普通窗口比较简单的了,它只会为普通客户服务,所以服务的客户类型只是普通客户
	 */
	public String serviceCustomerTypeName() {
		
		return "普通客户";
	}

	/**
	 * 普通窗口需要找取号机,找普通号码管理器要普通业务等待排队号码即可。不用管快速客户与vip客户了。 
	 */
	public Integer serviceNumber() {
		return NumberMachineServer.getInstance().getServiceNumber("COMMON");
	}

}


3. ExpressBusinessWindow 快速客户窗口类代码:

package com.isoftstone.bankcontroller;

/**
 * 快速客户窗口类,服务的客户类型名称是变化的,可能是快速客户也可能是普通客户。
 * 业务处理需要的时间,永远是最小业务时间,所以必须要重写覆盖掉serviceNeedHandingTime方法 
 * 当然需要实现,具体是如何去获取号码的
 * @author chen
 *
 */
public class ExpressBusinessWindow extends AbstractBusinessWindow {

	private String serviceCustomerTypeName = "快速客户";  //默认为快速客户进行服务
	
	/**
	 * 获取当前express窗口为,哪类客户服务的。
	 */
	@Override
	public  String serviceCustomerTypeName() {
		return serviceCustomerTypeName;
	}

	/**
	 * 快速窗口处理业务的时间,永远是最小的时间,哪所为普通客户进行服务时。
	 * 所以必须要重写父类的方法。因为父类的业务处理时间是为最小值时间和最大时间之间的随机数
	 * @return 返回最小业务处理时间
	 */
	@Override
	public int serviceNeedHandingTime() {
		int minHandingTime =  Integer.valueOf(ConfigReader.getInstance().readConfig("min_handing_business_time"));
		return minHandingTime;
	}

	/**
	 * 快速窗口先要去取自己客户等待的号,若此号没有取到,则去取普通客户等待的排队号码。
	 * @return 返回需要服务的号码,若快速客户号码找不到,则去找普通客户号码,两者都找不到则返回一个null值
	 */
	@Override
	public Integer serviceNumber() {
		NumberMachineServer numberMachineServer = NumberMachineServer.getInstance();
		Integer retNum = numberMachineServer.getServiceNumber("EXPRESS");//取自己客户等待的号
		if(null==retNum){
			//若没有取到自己的号码则需要取普通客户等待的排队号码
			System.out.println(this.getName() +",没有取到快速客户服务的任务");
			System.out.println(this.getName()+" 开始准备为普通客户服务,准备取普通客户服务");
			retNum = numberMachineServer.getServiceNumber("COMMON");
				serviceCustomerTypeName = "普通客户";  //为普通客户服务时,将服务客户的类型必成普通客户
		}else{
			serviceCustomerTypeName = "快速客户"; 
		}
		return retNum;
	}

}


4.VipBusinessWindow VIP客户窗口类代码:

package com.isoftstone.bankcontroller;

/**
 * VIP客户窗口类,服务的客户类型名称是变化的,可能是VIP客户也可能是普通客户。
 * 不需要覆盖serviceNeedHandingTime方法,因为本身来说也是在最小与最大业务时间之内随机产生的
 * 当然需要实现,具体是如何去获取号码的
 * @author chen
 *
 */
public class VipBusinessWindow extends AbstractBusinessWindow {

	private String serviceCustomerTypeName = "vip客户";  //默认为vip客户进行服务
	
	/**
	 * 获取当前vip窗口为,哪类客户服务的。
	 */
	@Override
	public String serviceCustomerTypeName() {
		return serviceCustomerTypeName;
	}

	
	/**
	 * vip窗口先要去取自己客户等待的号,若此号没有取到,则去取普通客户等待的排队号码。
	 * @return 返回需要服务的号码,若vip客户号码找不到,则去找普通客户号码,两者都找不到则返回一个null值
	 */
	@Override
	public Integer serviceNumber() {
		NumberMachineServer numberMachineServer = NumberMachineServer.getInstance();
		Integer retNum = numberMachineServer.getServiceNumber("VIP");//取自己客户等待的号
		if(null==retNum){
			//若没有取到自己的号码则需要取普通客户等待的排队号码
			System.out.println(this.getName() +",没有取到vip客户服务的任务");
			retNum = numberMachineServer.getServiceNumber("COMMON");
			System.out.println(this.getName()+" 开始准备为普通客户服务,准备取普通客户服务");
			serviceCustomerTypeName = "普通客户";  //为普通客户服务时,将服务客户的类型必成普通客户
		}else{
			serviceCustomerTypeName = "vip客户";
		}
		return retNum;
	}

}


5. ConfigReader类,配置读取器类,是从改进版本中的交通灯管理系统中直接拿来的。呵呵,看来多积累点代码还是很有好处滴。

package com.isoftstone.bankcontroller;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 单例模式,用于读取与程序相关的配置信息。
 * 
 * @author chen
 *
 */
public class ConfigReader {

	private Properties props ;
	
	private static final ConfigReader cr = new ConfigReader();
	
	/**
	 * 用单列模式,保证只有一份实例,以免创建过多实例对象,导致内存资源的不必要的浪费。
	 * @return
	 */
	public static ConfigReader getInstance(){
		return cr;
	}
	
	/**
	 * 根据提供的配置信息的Key,获取配置信息的值。
	 * @param key 配置信息的Key
	 * @return 返回配置信息中找到的值
	 */
	public String readConfig(String key){
		
		return props.getProperty(key);
	}
	
	/**
	 * 需要在构造方法中,初始化一些资料信息
	 */
	private ConfigReader(){
		props = new Properties();
		InputStream is = ConfigReader.class.getResourceAsStream("bank_conf.properties");
		try {
			props.load(is);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	
}


6. NumberMachineServer 取机器类,其中包含自己的内部类 号码管理器类NumberManager 代码:

package com.isoftstone.bankcontroller;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 号码机,提供获取号码与生成号码的提供。
 * 内部拥有个号码管理器对象池,根据来找号码机服务的客户窗口类型对应的号码类型不同
 * 找到对象的号码管理器为其,提供服务功能。
 * 
 * 其实上消费者来说,并不需要知道有号码管理器的生成,如果需要生成号码取号排队,找取号机生成就行。
 * 如果需要取出正在排队的首位号码,去为此号码服务时,找取号机就可以了。
 * @author chen
 *
 */
public class NumberMachineServer {

	/**
	 * maps 对象池,根据客户窗口类型不同,创建出不同对象存入其中。
	 * 下次来了相同客户窗口类型,只需要将对象从中取出给,用取此的对象进行处理号码操作即可。
	 */
	private Map<String,NumberManager> maps ;
	
	private static final NumberMachineServer nms = new NumberMachineServer();
	
	//显然也是用到了单例设计模式,不允许取号器服务器,有多个实例着。
	private NumberMachineServer(){
		maps = new TreeMap<String,NumberManager>();
		//number_types 在配置文件中存储着总共有多少种号码类型,中间用逗号进行隔开.
		String strNumberManagers = ConfigReader.getInstance().readConfig("number_types");
		String[] numberManagers = strNumberManagers.split(",");
		
		//初始化,将所有号码管理员new出来放入到对象池中。用map来代表池呢
		for(String numberManage:numberManagers){
			NumberManager nm = new NumberManager();
			maps.put(numberManage, nm);
		}
	}
	
	/**
	 * 获得取号机实例,得到的实例对象是单例的,只存在一份实例对象。
	 * @return
	 */
	public static NumberMachineServer getInstance(){
		return nms;
	}
	
	/**
	 * 通过取号机中的号码管理器池获取一个号码管理器,用此号码管理用,去生成一个号码
	 * @param numberType 客户窗口类型对应的号码类型
	 * @return 返回生成的号码
	 */
	public Integer generataNewNumber(String numberType){
		NumberManager numberManager = maps.get(numberType);
		if(null == numberManager)  //得不到号码管理器说明传入的参数不对,要求停下来检查下代码是否有问题呢
			throw new RuntimeException("不存在"+numberManager+"类型的客户窗口,无法取号");
		return numberManager.generataNewNumber();  //调用相应号码管理器对象的,生成一个号码
		
	}
	
	/**
	 * 通过取号机中的号码管理器池获取一个号码管理器,用此号码管理用,去取得一个号码
	 * @param numberType 客户窗口类型对应的号码类型
	 * @return 返回准备去待服务的号码
	 */
	public Integer getServiceNumber(String numberType){
		NumberManager numberManager = maps.get(numberType);
		if(null == numberManager)  //得不到号码管理器说明传入的参数不对,要求停下来检查下代码是否有问题呢
			throw new RuntimeException("不存在"+numberManager+"类型的客户窗口,无法取号");
		return numberManager.getServiceNumber(); //调用相应客户窗口类型的号码管理员,获取一个号码去准备为客户服务。
	}
	
	/**
	 * 号码管理器,用于管理来用户时,取号。
	 * 那么就发一个号给客户。
	 * 
	 * 窗口服务时就提供一个号。
	 * 由于有三类服务窗口,所以这里只需要三个实例对象,每个对象独立保留自己目前产生的号是多少了。
	 * 比如: 普通_3号、快速_2号、vip_1号 
	 * 
	 * 由于只产生三个实例对象,分别给三种业务窗口和客户服务得,为了不让外界new出来多个实例变量。
	 * 
	 * 但需要考虑将来的扩展,并不一定非得是三个类型窗口和客户。
	 * 
	 * 这里将此内部化掉,对象只公布一个取号机。而这个取号机中可以new对象,其它外面类不可new对象。取号直接找取号机即可。
	 * 由取号机,调用号码管理器,实际发一个号码给客户或给一个等待处理的号给相应的窗口。
	 *
	 * @author chen
	 *
	 */
	private class NumberManager {

			private List<Integer> nums = new ArrayList<Integer>();
			
			private int count;
			
			/**
			 * 取走生成的这一个号码
			 * 由于同一类的客户窗口,可能不止一个如普通客户窗口,有3个呢。所以最好同步下,别一起add了。
			 * @return 返回生成的这一个号码
			 */
			public synchronized Integer generataNewNumber(){
				nums.add(++count);
				return count;
			}
			
			/**
			 * 取走一个正在排队等候首位的号码。
			 * 由于同一类的客户窗口,可能不止一个如普通客户窗口,有3个呢。所以最好同步下,别一起remove了。
			 * @return 返回一个正在排队等候首位的号码。
			 */
			public synchronized Integer getServiceNumber(){
				if(nums.size()>0)
					return nums.remove(0);
				return null;
			}
	}
}

7.配置文件:

#银行系统配置文件 

#################################业务处理时间配置部分#################################
#最大业务处理时间为10秒。
max_handing_business_time=10
#最大业务处理时间为1秒。
min_handing_business_time=1


#################################客户自动生成相应的时间配置#################################
#普通客户生成初始化等待时间
common_customer_initial_delay=0
#快速客户生成初始化等待时间
express_customer_initial_delay=0
#vip客户生成初始化等待时间
vip_customer_initial_delay=0

#普通客户生成时间间隔基数,最后的生成时间为基数*时间间隔
common_customer_base_period=1
#快速客户生成时间间隔基数,最后的生成时间为基数*时间间隔
express_customer_base_period=2
#vip客户生成时间间隔基数,最后的生成时间为基数*时间间隔
vip_customer_base_period=6

#普通客户生成时间间隔
common_customer_period=1

#指定程序中有几种窗口对象类型,相应的取号机中就维护这几种类型的每一种类型,一个号码管理器
number_types=COMMON,EXPRESS,VIP


8. 测试Client类代码:

package com.isoftstone.bankcontroller;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 测试运行类,这里还可以加入新的客户窗口,只需要实现一个AbstractBusinessWindow类
 * 并将配置文件中number_types键中配置加入新的号码类型标识。
 * 客户端在 new 新的客户类型窗口, new 新的客户,要新的类型标识即可了呢。
 * @author chen
 *
 */
public class Client {
	
	public static void main(String[] args){
		
		for(int i=1;i<5;i++){
			AbstractBusinessWindow commonWindow = new CommonBusinessWindow();
			commonWindow.setName("普通窗口__"+i);
			commonWindow.service();
		}
		
		AbstractBusinessWindow expressWindow = new ExpressBusinessWindow();
		expressWindow.setName("快速窗口__1");
		expressWindow.service();
		
		AbstractBusinessWindow vipWindow = new VipBusinessWindow();
		vipWindow.setName("vip窗口__1");
		vipWindow.service();
		
		//读取配置信息中,生成普通客户、快速客户、vip客户初始化的等待时间
		int commonCustomerInitialDelay = Integer.valueOf(ConfigReader.getInstance().readConfig("common_customer_initial_delay"));
		int expressCustomerInitialDelay = Integer.valueOf(ConfigReader.getInstance().readConfig("express_customer_initial_delay"));
		int vipCustomerInitialDelay = Integer.valueOf(ConfigReader.getInstance().readConfig("vip_customer_initial_delay"));
		//读取配置信息中,普通客户、快速客户、vip客户生成的基数时间,具体时间为基数比例时间*普通客户生成的时间
		int commonCustomerBasePeriod = Integer.valueOf(ConfigReader.getInstance().readConfig("common_customer_base_period"));
		int expressCustomerBasePeriod = Integer.valueOf(ConfigReader.getInstance().readConfig("express_customer_base_period"));
		int vipCustomerBasePeriod = Integer.valueOf(ConfigReader.getInstance().readConfig("vip_customer_base_period"));
		//读取配置信息中,普通客户生成的时间
		int commonCustomerPeriod = Integer.valueOf(ConfigReader.getInstance().readConfig("common_customer_period"));
		
		//计算普通客户需要生成的间隔时间
		int commonPeriod = commonCustomerPeriod*commonCustomerBasePeriod;
		//计算快速客户需要生成的间隔时间
		int expressPeriod = commonCustomerPeriod*expressCustomerBasePeriod;
		//计算vip客户需要生成的间隔时间
		int vipPeriod = commonCustomerPeriod*vipCustomerBasePeriod;
		
		
		Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						int num = NumberMachineServer.getInstance().generataNewNumber("COMMON");//普通客户需要提供,普通客户的号码类型标识
						System.out.println("__普通客户__"+num+",来了正在等待排队中");
					}
				}, 
				commonCustomerInitialDelay, 
				commonPeriod, 
				TimeUnit.SECONDS);
		
		Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						int num = NumberMachineServer.getInstance().generataNewNumber("EXPRESS");//快速客户需要提供,快速客户的号码类型标识
						System.out.println("__快速客户__"+num+",来了正在等待排队中");
					}
				}, 
				expressCustomerInitialDelay, 
				expressPeriod, 
				TimeUnit.SECONDS);
		
		
		Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						int num = NumberMachineServer.getInstance().generataNewNumber("VIP");//VIP客户需要提供,VIP客户的号码类型标识
						System.out.println("__vip客户__"+num+",来了正在等待排队中");
					}
				}, 
				vipCustomerInitialDelay, 
				vipPeriod, 
				TimeUnit.SECONDS);
	}

}

/**
 *  运行结果如设想结果一致:
 *  普通窗口__1 服务准备就绪已经开始叫号
	普通窗口__3 服务准备就绪已经开始叫号
	快速窗口__1 服务准备就绪已经开始叫号
	普通窗口__2 服务准备就绪已经开始叫号
	普通窗口__4 服务准备就绪已经开始叫号
	vip窗口__1 服务准备就绪已经开始叫号
	普通窗口__1,没有取到普通客户 服务任务,先休息一秒种
	__vip客户__1,来了正在等待排队中
	__快速客户__1,来了正在等待排队中
	__普通客户__1,来了正在等待排队中
	普通窗口__4,为普通客户__1服务中...
	普通窗口__2,没有取到普通客户 服务任务,先休息一秒种
	vip窗口__1,为vip客户__1服务中...
	普通窗口__3,没有取到普通客户 服务任务,先休息一秒种
	快速窗口__1,为快速客户__1服务中...
	__普通客户__2,来了正在等待排队中
	普通窗口__1 服务准备就绪已经开始叫号
	普通窗口__1,为普通客户__2服务中...
	普通窗口__2 服务准备就绪已经开始叫号
	普通窗口__2,没有取到普通客户 服务任务,先休息一秒种
	快速窗口__1,为快速客户__1服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,没有取到普通客户 服务任务,先休息一秒种
	普通窗口__3 服务准备就绪已经开始叫号
	普通窗口__3,没有取到普通客户 服务任务,先休息一秒种
	__普通客户__3,来了正在等待排队中
	__快速客户__2,来了正在等待排队中
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__2服务中...
	普通窗口__2 服务准备就绪已经开始叫号
	普通窗口__2,为普通客户__3服务中...
	普通窗口__3 服务准备就绪已经开始叫号
	普通窗口__3,没有取到普通客户 服务任务,先休息一秒种
	__普通客户__4,来了正在等待排队中
	快速窗口__1,为快速客户__2服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,为普通客户__4服务中...   -----------------------------------------快速空起来,逮到有普通客户等待,为普通客户服务了
	普通窗口__3 服务准备就绪已经开始叫号
	普通窗口__3,没有取到普通客户 服务任务,先休息一秒种
	__普通客户__5,来了正在等待排队中
	__快速客户__3,来了正在等待排队中
	快速窗口__1,为普通客户__4服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__3服务中...
	普通窗口__4,为普通客户__1服务完成,共耗时4秒
	普通窗口__4 服务准备就绪已经开始叫号
	普通窗口__4,为普通客户__5服务中...
	普通窗口__3 服务准备就绪已经开始叫号
	普通窗口__3,没有取到普通客户 服务任务,先休息一秒种
	__普通客户__6,来了正在等待排队中
	普通窗口__1,为普通客户__2服务完成,共耗时4秒
	普通窗口__1 服务准备就绪已经开始叫号
	普通窗口__1,为普通客户__6服务中...
	快速窗口__1,为快速客户__3服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,没有取到普通客户 服务任务,先休息一秒种
	普通窗口__3 服务准备就绪已经开始叫号
	普通窗口__3,没有取到普通客户 服务任务,先休息一秒种
	__普通客户__7,来了正在等待排队中
	__快速客户__4,来了正在等待排队中
	__vip客户__2,来了正在等待排队中
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__4服务中...
	普通窗口__3 服务准备就绪已经开始叫号
	普通窗口__3,为普通客户__7服务中...
	__普通客户__8,来了正在等待排队中
	快速窗口__1,为快速客户__4服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,为普通客户__8服务中... -----------------------------------快速空起来,逮到有普通客户等待,为普通客户服务了
	__普通客户__9,来了正在等待排队中
	__快速客户__5,来了正在等待排队中
	普通窗口__1,为普通客户__6服务完成,共耗时3秒
	普通窗口__1 服务准备就绪已经开始叫号
	普通窗口__1,为普通客户__9服务中...
	普通窗口__4,为普通客户__5服务完成,共耗时4秒
	普通窗口__4 服务准备就绪已经开始叫号
	普通窗口__4,没有取到普通客户 服务任务,先休息一秒种
	快速窗口__1,为普通客户__8服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__5服务中...
	vip窗口__1,为vip客户__1服务完成,共耗时8秒
	vip窗口__1 服务准备就绪已经开始叫号
	vip窗口__1,为vip客户__2服务中...
	__普通客户__10,来了正在等待排队中
	普通窗口__4 服务准备就绪已经开始叫号
	普通窗口__4,为普通客户__10服务中...
	快速窗口__1,为快速客户__5服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,没有取到普通客户 服务任务,先休息一秒种
	__普通客户__11,来了正在等待排队中
	__快速客户__6,来了正在等待排队中
	普通窗口__1,为普通客户__9服务完成,共耗时2秒
	普通窗口__1 服务准备就绪已经开始叫号
	普通窗口__1,为普通客户__11服务中...
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__6服务中...
	__普通客户__12,来了正在等待排队中
	普通窗口__2,为普通客户__3服务完成,共耗时9秒
	普通窗口__2 服务准备就绪已经开始叫号
	普通窗口__2,为普通客户__12服务中...
	快速窗口__1,为快速客户__6服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,没有取到普通客户 服务任务,先休息一秒种
	__普通客户__13,来了正在等待排队中
	__快速客户__7,来了正在等待排队中
	__vip客户__3,来了正在等待排队中
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__7服务中...
	__普通客户__14,来了正在等待排队中
	快速窗口__1,为快速客户__7服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,为普通客户__13服务中...
	__普通客户__15,来了正在等待排队中
	__快速客户__8,来了正在等待排队中
	快速窗口__1,为普通客户__13服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__8服务中...
	普通窗口__3,为普通客户__7服务完成,共耗时8秒
	普通窗口__3 服务准备就绪已经开始叫号
	普通窗口__3,为普通客户__14服务中...
	__普通客户__16,来了正在等待排队中
	快速窗口__1,为快速客户__8服务完成,共耗时1秒
	vip窗口__1,为vip客户__2服务完成,共耗时7秒
	vip窗口__1 服务准备就绪已经开始叫号
	vip窗口__1,为vip客户__3服务中...
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,为普通客户__15服务中...
	__普通客户__17,来了正在等待排队中
	__快速客户__9,来了正在等待排队中
	快速窗口__1,为普通客户__15服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__9服务中...
	__普通客户__18,来了正在等待排队中
	vip窗口__1,为vip客户__3服务完成,共耗时2秒
	vip窗口__1 服务准备就绪已经开始叫号
	vip窗口__1,没有取到vip客户服务的任务
	快速窗口__1,为快速客户__9服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	vip窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	vip窗口__1,为普通客户__16服务中... ------------------------------VIP空起来,逮到有普通客户等待,为普通客户服务了
	快速窗口__1,为普通客户__17服务中...
	__普通客户__19,来了正在等待排队中
	__快速客户__10,来了正在等待排队中
	__vip客户__4,来了正在等待排队中
	快速窗口__1,为普通客户__17服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__10服务中...
	普通窗口__4,为普通客户__10服务完成,共耗时9秒
	普通窗口__4 服务准备就绪已经开始叫号
	普通窗口__4,为普通客户__18服务中...
	普通窗口__3,为普通客户__14服务完成,共耗时4秒
	普通窗口__3 服务准备就绪已经开始叫号
	普通窗口__3,为普通客户__19服务中...
	__普通客户__20,来了正在等待排队中
	普通窗口__2,为普通客户__12服务完成,共耗时8秒
	普通窗口__2 服务准备就绪已经开始叫号
	普通窗口__2,为普通客户__20服务中...
	快速窗口__1,为快速客户__10服务完成,共耗时1秒
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,没有取到快速客户服务的任务
	快速窗口__1 开始准备为普通客户服务,准备取普通客户服务
	快速窗口__1,没有取到普通客户 服务任务,先休息一秒种
	__普通客户__21,来了正在等待排队中
	__快速客户__11,来了正在等待排队中
	普通窗口__1,为普通客户__11服务完成,共耗时10秒
	普通窗口__1 服务准备就绪已经开始叫号
	普通窗口__1,为普通客户__21服务中...
	快速窗口__1 服务准备就绪已经开始叫号
	快速窗口__1,为快速客户__11服务中...
	__普通客户__22,来了正在等待排队中
	vip窗口__1,为普通客户__16服务完成,共耗时4秒
	vip窗口__1 服务准备就绪已经开始叫号
	vip窗口__1,为vip客户__4服务中...
 */


结束语:

其实设计模式中用很多值去借鉴的,所以没有讲出来具体给读者分析这些模式。但相信读完此遍的读者,肯定可以看到明显的有模板方式模式,

比如AbstractBusinessWindow类中的 startService()方法,各个窗口给出的功能骨架是一致的,但细节又不同比如服务的客户类型名不同,取出号码的过程也不同(可以取自己窗口客户类型号,也可以取普通的快速与Vip就可以),需要业务处理的时间也不同。而其它的处理业务的流程是一致的。这时候我们可以想到用模板方法设计模式了。

模板方式设计应用在 定义功能时,功能的一部分是确定的。而另一部分不确认,确认的部分使用不确认的部分(或是顺利接连执行着),那么就可以把不确定的功能暴露出来。由子实完成。

另一设计模式,共享设计模式不是可以从NumberMachineServer类中的Map实例看出,它是一个NumberManager对象池,可以将对象存入起来共享给大家用。减少内存的不必要浪费,当前时面也是形式所逼不得,new多现多个NumberManger,每种客户窗口类型也只有一个唯一的号码管理器对象去服务。关于共享设计模式,已经在上遍http://blog.csdn.net/chenshufei2/article/details/7837802中,讲到了,这里就不再废话了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值