黑马程序员--7k之银行业务调度系统

来源:互联网 发布:矩阵行列式 编辑:程序博客网 时间:2024/06/06 02:44

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------


银行业务调度系统

需求:

模拟实现银行业务调度系统逻辑,具体需求如下:

 

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

 

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

 

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

 

        VIP客户 :普通客户 :快速客户 =  1 :6 :3。

 

Ø 客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。

 

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

 

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

 

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

 

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



需求分析:

生成的排队号码有三类:普通用户,快速用户,VIP用户。

三种号码是分开的,也就是号码生成器能生成三类独立的号码,三类号码的序号分别递增,互不影响。由此可以设计一个号码生成类NumberManager,用于生成号码,由于三种号码的生成逻辑一样,所以只用设计一个类即可。考虑到要生成三种类型的号码,所以可以创建三个NumberManager对象。


在NumberManager类中,有号码,就要有操作号码的方法:生成号码,为业务窗口提供号码。


一开始写的时候是这样写的,感觉很对,但是这样的话是肯定会出问题的。

原因就在于这个类里面提供的两个操作号码的方法是多线程操作的,生成号码一个线程,窗口取号码是一个线程,这两个线程所使用的号码是同一份。所以必须使用同步,不然就会出现安全问题。



这样就保证了共享数据的安全性。


在上面的fetchNumber方法中还要注意一点:业务窗口每取到一次号码,就要在号码集合中移除一个,每次取得都是最前面的号码,就是集合中的第0个号码,集合的remove方法不仅删除数据,并且会返回删除的数据,利用这一点,窗口取号和号码生成器中的号码集合删除号码的动作就合二为一,这是个小技巧。既方便编程,又保证数据安全。



NumberMachine类:号码生成机器

只有一个产生号码的机器,所以要用单例。

单例设计方法:把构造方法设置为private,有一个静态方法返回自身对象。

由于构造方法私有,所以在类之外不能创建该类对象,但在类之内可以创建对象。在类之内创建一个私有的类对象,写一个静态方法,静态方法返回创建的这个对象。这个对象是全局的,而且只有一个。这就是单例设计方法。

        private NumberMachine(){}      //构造方法,私有


        private static NumberMachine instance = new NumberMachine();


        public static NumberMachine getInstance()

       {
                  return instance;   //返回对象,所以可以通过getInstance()方法生成一个对象
        }



再来分析业务窗口,业务窗口会有一个向号码生成器索要当前排队号码的活动,并且在银行上班期间,这个活动一直持续不会停,所以要单独给要号这个动作创建一个线程,让这个线程一直运行。

public void start(){
        Executors.newSingleThreadExecutor().execute(
                  new Runnable(){               //这种匿名内部类表示创建一个内部类,该类实现Runnable接口。
                          public void run(){
                                 while(true){
                                        switch(type){
                                              case COMMON:
                                                          commonService();
                                                          break;
                                              case EXPRESS:
                                                          expressService();
                                                          break;
                                              case VIP:
                                                          vipService();
                                                          break;
                                      }
                               }
                        }
                }
       );

 } 


创建线程用到java5中的新的API:Executors。

该类创建一个线程池,当要执行某一任务时,把任务交给线程池,线程池内有很多线程,从内部找一个线程来执行该任务,这样做的好处就是可以提高效率。


一共有三种类型的客户,而且唯一确定:普通客户,快速客户,VIP客户。

所以想到用枚举,创建一个枚举客户类,类中有三个实例:


public enum CustomerType {
COMMON,EXPRESS,VIP;
public String toString(){
String name = null;
switch(this){
case COMMON:
name = "普通";
break;
case EXPRESS:
name = "快速";
break;
case VIP:
name = name();
break;
}
return name;
}
}


覆盖了toString()方法的原因是希望返回中文。枚举类有一个name()方法,返回枚举实例的名字。


再回来分析业务窗口,实现业务:

new Runnable(){
public void run(){

while(true){
switch(type){
case COMMON:
commonService();
break;
case EXPRESS:
expressService();
break;
case VIP:
vipService();
break;
}
}
}
}



普通窗口向号码生成器取号

如果取到的号不为空,表明当前有人排队,则模拟为客户服务,让线程睡1-10秒。



快速窗口向号码生成器取号

如果取到的号不为空,表明当前有人排队,则模拟为客户服务,让线程睡1秒。

如果取不到号码,表明当前没有快速客户排队,则为普通客户服务,向普通用户号码生成器取号。



VIP窗口的业务逻辑和快速窗口的业务逻辑一样。





在MainClass中,先启动服务窗口的线程开始向号码生成器取号


//产生4个普通窗口
for(int i=1;i<5;i++){
ServiceWindow window =  new ServiceWindow();
window.setNumber(i);
window.start();
}

//产生1个快速窗口
ServiceWindow expressWindow =  new ServiceWindow();
expressWindow.setType(CustomerType.EXPRESS);
expressWindow.start();

//产生1个VIP窗口
ServiceWindow vipWindow =  new ServiceWindow();
vipWindow.setType(CustomerType.VIP);
vipWindow.start();


然后开始出现客户:

有三种客户,比例为:

VIP客户 :普通客户 :快速客户 =  1 :6 :3。

所以创建并启动三个线程,分别产生普通号,快速号,VIP号。

Executors.newScheduledThreadPool(1).scheduleAtFixedRate();

Executors.newScheduledThreadPool(1).scheduleAtFixedRate();

Executors.newScheduledThreadPool(1).scheduleAtFixedRate();


这种线程是带定时器的线程。

线程的执行代码中向客户集合中增加客户,由于三类客户的比例为1:6:3,也就是同样的时间,来的普通客户是快速客户的2倍,是VIP客户的6倍。

也就是说,如果m秒来一个普通客户,则2m秒才来一个快速客户,6m秒来一个VIP客户。

所以在三个计数器线程里面,三个时间间隔的比值为1:2:6。


其实这种java1.5的线程API的效果,用原来的API也可以实现,无非就是用一个while(true)语句,实现循环。在线程里面,通过让线程睡眠的方法设置时间间隔。

虽然在编程时都能达到效果,但是只好是用新的技术,因为新的技术是对原来旧的技术的改良,改良的效果是提高了效率。



在这个项目中,用到的知识点有:集合,单例,枚举,线程。

我觉得之所以有难度,是因为现在还没有形成面向对象程序设计的思想,在听张孝祥老师的课时,张老师说过,面向对象的设计思想是通过不断地积累经验而形成的,刚开始要去模仿别人的编程风格,久而久之自然形成这种习惯,也能在看到需求时,很快的分析出来面向对象设计的流程。


面向对象程序设计的一个规则:谁拥有数据,对数据进行操作的方法就由谁提供。

通过这个项目的学习,理解面向对象程序设计的思想,积累面向对象程序设计的经验。




0 0