黑馬程序員——銀行業務調度系統

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity開發</a>、<a href="http://www.itheima.com"target="blank">.Net培訓</a>、期待與您交流! ----------------------

1、銀行調度業務系統需求

銀行業務調度系統:

模擬實現銀行業務調度系統邏輯,具體需求如下:

1、銀行內有6個業務窗口,1-4號窗口爲普通窗口,5號窗口爲快速窗口,6號窗口爲VIP窗口;

2、有三種對應類型的客戶:VIP客戶,普通客戶,快速客戶(辦理如交水電費,電話費之類業務的客戶);

3、異步隨機生成各種類型客戶,生成各類型用戶的概率比例爲:

VIP客戶:普通客戶:快速客戶 = 1:6:3;

4、客戶辦理業務所需時間有最大值和最小值,在該範圍內隨機設定每個VIP客戶以及普通客戶辦理業務所需的時間,快速客戶辦理業務所需時間爲最小值(提示:辦理業務的過程可通過線程Sleep的方式模擬)

5、各類型客戶在其對應窗口按順序依次辦理業務;

6、當VIP(6號)窗口和快速業務(5號)窗口沒有客戶等待辦理業務時,這兩個窗口可以處理普通客戶的業務,而一旦有對應的客戶等待辦理業務的時候,則優先處理對應客戶的業務;

7、隨機生成客戶時間間隔以及業務辦理時間最大值和最小值自定,可以設置;

8、不要求實現GUI,只考慮系統邏輯實現,可以通過Log方式展現程序運行結果。

  

2 、面向對象分析設計

1、有三種對應類型的客戶:VIP客戶,普通客戶,快速客戶;異步隨機生成各種類型的客戶,各類型客戶在其對應窗口按順序依次辦理業務。

         (1) 首先,經常在銀行辦理業務的人更有利於理解本系統。每一個客戶其實就是由銀行的一個取號機器產生號碼的方式來表示的,所以,我們想要有一個號碼管理器對象,讓這個對象不斷的產生號碼,就等於隨機生成了客戶;

         (2)  由於有三種類型客戶,每類客戶的號碼編排都是完全獨立的,所以,我想到本系統一共要產生3個號碼管理器對象,各自管理一類用戶的排隊號碼。這三個號碼管理器對象統一由一個號碼機器進行管理,這個號碼機器在整個系統中始終只能有一個,所以,它要被設計成單例。

2、各類型客戶在其對應窗口按順序依次辦理業務,準確的說,應該是窗口依次叫號;

        (1)、各個窗口怎麼知道該叫那一個號了呢?它一定是問的對應的號碼管理器,即服務窗口每次找號碼管理器獲取當前要被服務的號碼;

        (2)、如果不是多次親身經歷銀行的這種業務,再加上積累大量的面向對象開發的應用經驗,不可能輕鬆的進行這種設計,能否發掘出其中隱含的對象信息,還要看日積月累出來的感覺。

類圖進行分析:

 

                          

編碼如下


1、號碼管理器類(NumberManager類

定義一個用於存儲上一個客戶號碼的成員變量和用於存儲所有等待服務的客戶號碼的隊列集合。
定義一個產生新號碼的方法和獲取馬上要爲之服務的號碼的方法,這兩個方法被不同的線程操作了相同的數據,所以,要進行同步。

import java.util.ArrayList;
import java.util.List;
/**
 * NumberManager:號碼管理器(可理解爲銀行掛號機器中的一個分支)
 * 當客戶來了要生成一個號碼,並加在對應客戶類型的集合中;
 * 服務窗口會找對應的取號機取號去服務。
 * 因爲生成號碼和取走號碼同時用到成員變量,但又是不同的線程,需要加同步
 */
publicclass NumberManager
{
       privateintlastNumber = 1;    //記住上一個號碼
       private List<Integer> queueNumber = new ArrayList<Integer>();
      
       //客戶來了生成號碼
       publicsynchronized Integer generateNewManager()
       {
              //來一個客戶就往集合中加一個號碼
              queueNumber.add(lastNumber);  
              returnlastNumber++;  
       }
      
       //服務窗口獲取號碼
       publicsynchronized Integer fetchServiceNumber()
       {
              Integer number = null;
              if(queueNumber.size()>0)
              {
                     //獲取集合中第一個號碼,因爲第一個號碼是最先加進來並且沒取走的
                     number = queueNumber.remove(0);
              }
              return number;
       }
      
}

2、號碼機器的類NumberMachine

定義三個成員變量分別指向三個NumberManager對象,分別表示普通、快速和VIP客戶的號碼管理器,定義三個對應的方法來返回這三個NumberManager對象。
將NumberMachine類設計成單例

/**
 * NumberMachine:號碼機器(相當於我們去銀行用的掛號機器)
 * 會返回三個號碼管理器;
 * 號碼機器在系統中只能有一個,要不然就會有幾個相同的號碼機器,窗口不知道去哪個號碼機上拿號碼
 */
publicclass NumberMachine
{
       //三種類型的客戶(普通,快速,vip)
       private NumberManager commonManager = new NumberManager();
       private NumberManager expressManager = new NumberManager();
       private NumberManager vipManager = new NumberManager();
      
       //get方法
       public NumberManager getCommonManager() {
              returncommonManager;
       }
       public NumberManager getExpressManager() {
              returnexpressManager;
       }
       public NumberManager getVipManager() {
              returnvipManager;
       }
      
       //單例設計模式中構造方法私有
       private NumberMachine(){}
       //單例設計模式,返回一個對象
       publicstatic NumberMachine getInstance()
       {
              returninstance;
       }
       privatestatic NumberMachine instance = new NumberMachine();;
      
}

3、客戶類型(CustomerType枚舉類

系統中有三種類型的客戶,所以用定義一個枚舉類,其中定義三個成員分別表示三種類型的客戶。
重寫toString方法,返回類型的中文名稱。這是在後面編碼時重構出來的,剛開始不用考慮。

/**
 * 客戶類型(枚舉):CustomerType
 */
 
publicenum CustomerType
{
       COMMON,EXPRESS,VIP;
      
       public String toString()
       {
              switch(this)
              {
              caseCOMMON:
                     return"普通";
              caseEXPRESS:
                     return"快速";
              caseVIP:
                     return name();
              }
              returnnull;
       }
}

4、業務窗口類ServiceWindow類

定義一個start方法,內部啓動一個線程,根據服務窗口的類別分別循環調用三個不同的方法。 
定義三個方法分別對三種客戶進行服務,爲了觀察運行效果,應詳細打印出其中的細節信息

import java.util.Random;
import java.util.concurrent.Executors;
 
/**
 * 服務窗口類:ServiceWindow
 * 有兩個屬性,一個是窗口類型,一個是窗口號
 */
publicclass ServiceWindow
{
       //代表窗口類型(3種)枚舉。默認普通,因爲普通最多
       private CustomerType type= CustomerType.COMMON;  
       privateintwindowId = 1;   //窗口號
      
       //爲什麼只要get方法,因爲以後可以設置窗口類型及號碼,不要一修了窗口以後就不能更改了
       publicvoid setType(CustomerType type) {
              this.type = type;
       }
       publicvoid setWindowId(int windowId) {
              this.windowId = windowId;
       }
 
       publicvoid start()
       {
              Executors.newSingleThreadExecutor().execute(new Runnable(){
                     publicvoid run()
                     {
                            while(true)
                            {
                                   /*if(type==CustomerType.COMMON)
                                   NumberMachine.getInstance().getCommonManager();
                                   else if()
                                   else if()*/
                    //此處用switch更高效
                                   switch(type)  //表達式中可放的類型byte short int char enum
                                   {
                                   //向號碼管理器要號碼,也就是要服務
                                          caseCOMMON:
                                                 commonService();
                                                 break;
                                          caseEXPRESS:
                                                 expressService();
                                                 break;
                                          caseVIP:
                                                 vipService();
                                                 break;
                                   }
                            }
                     }            
              });
       }
 
                     //爲普通客戶服務
                     privatevoid commonService() {
                            //此處不能改爲普通。因爲VIP和快速窗口沒任務是會來爲普通服務
                            String windowName = "第"+windowId+"號"+type+"窗口";
                            Integer number = NumberMachine.getInstance().getCommonManager().fetchServiceNumber();
                            System.out.println(windowName+"正在獲取任務");
                            if(number!=null){
                                   System.out.println(windowName+"正在爲第"+number+"個"+"普通客戶服務");
                                   long beginTime = System.currentTimeMillis();
                                   int maxRand = Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
                                   //服務時間1000-10000毫秒
                                   long serveTime = new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;
                                   try {
                                          //線程睡幾秒就好像爲客戶服務的時間
                                          Thread.sleep(serveTime);
                                   } catch (InterruptedException e) {
                                          e.printStackTrace();
                                   }
                                   long costTime = System.currentTimeMillis()-beginTime;
                                   System.out.println(windowName+"爲第"+number+"個"+"普通客戶完成服務,耗時"+costTime/1000+"秒");
                            }else{
                                   System.out.println(windowName+"沒有取到任務,先休息1秒鐘嘛");
                                   try {
                                          Thread.sleep(1000);
                                   } catch (InterruptedException e) {
                                          e.printStackTrace();
                                   }
                            }
                     }
 
                     //爲快速客戶服務
                     privatevoid expressService() {
                            String windowName = "第"+windowId+"號"+type+"窗口";
                            Integer number = NumberMachine.getInstance().getExpressManager().fetchServiceNumber();
                            System.out.println(windowName+"正在獲取任務");
                            if(number!=null){
                                   System.out.println(windowName+"正在爲第"+number+"個"+type+"客戶服務");
                                   long beginTime = System.currentTimeMillis();
                                   //int maxRand = Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
                                   //long serveTime = new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;
                                   try {
                                          //快速客戶只要最短的時間
                                          Thread.sleep(Constants.MIN_SERVICE_TIME);
                                   } catch (InterruptedException e) {
                                          e.printStackTrace();
                                   }
                                   long costTime = System.currentTimeMillis()-beginTime;
                                   System.out.println(windowName+"爲第"+number+"個"+type+"客戶完成服務,耗時"+costTime/1000+"秒");
                            }else{
                                   System.out.println(windowName+"沒有取到任務!");
                                   //快速窗口和VIP窗口沒有獲取到任務就去服務普通客戶
                                   commonService();
                            }
                     }
                    
                     //爲VIP客戶服務
                     privatevoid vipService() {
                            String windowName = "第"+windowId+"號"+type+"窗口";
                            Integer number = NumberMachine.getInstance().getVipManager().fetchServiceNumber();
                            System.out.println(windowName+"正在獲取任務");
                            if(number!=null){
                                   System.out.println(windowName+"正在爲第"+number+"個"+type+"客戶服務");
                                   long beginTime = System.currentTimeMillis();
                                   int maxRand = Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
                                   long serveTime = new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;
                                   try {
                                          Thread.sleep(serveTime);
                                   } catch (InterruptedException e) {
                                          e.printStackTrace();
                                   }
                                   long costTime = System.currentTimeMillis()-beginTime;
                                   System.out.println(windowName+"爲第"+number+"個"+type+"客戶完成服務,耗時"+costTime/1000+"秒");
                            }else{
                                   System.out.println(windowName+"沒有取到任務!");
                                   commonService();
                            }
                     }
}

5、主類(MainClass

用for循環創建出4個普通窗口,再創建出1個快速窗口和一個VIP窗口。
接着再創建三個定時器,分別定時去創建新的普通客戶號碼、新的快速客戶號碼、新的VIP客戶號碼。

<span style="font-size:18px;">import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
 
publicclass MainClass
{
       publicstaticvoid main(String[] args)
       {
              //4個普通窗口
              for(int i=1;i<5;i++)
              {
                     ServiceWindow commonWindow = new ServiceWindow();
                     commonWindow.setWindowId(i);  //設置窗口號
                     commonWindow.start();    //開始服務
              }
              //快速窗口
              ServiceWindow expressWindow = new ServiceWindow();
              expressWindow.setType(CustomerType.EXPRESS);  //設置窗口號
              expressWindow.start();
              //vip窗口
              ServiceWindow vipWindow = new ServiceWindow();
              vipWindow.setType(CustomerType.VIP);
              //vipWindow.setWindowId(6); 
              vipWindow.start();  
             
              //模擬客戶來服務,往數組中添加號碼
              Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
                            new Runnable(){
                                   publicvoid run()
                                   {
                                          Integer number = NumberMachine.getInstance().getCommonManager().generateNewManager();
                                          System.out.println(number+"號普通客戶等待服務!");
                                   }},
                            0,
                            Constants.COMMON_CUSTOMER_INTERVAL_TIME,
                            TimeUnit.SECONDS);
             
              Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
                            new Runnable(){
                                   publicvoid run()
                                   {
                                          Integer number = NumberMachine.getInstance().getVipManager().generateNewManager();
                                          System.out.println(number+"號VIP客戶等待服務!");
                                   }},
                            0,
                            //vip:普通客戶爲1:6
                            Constants.COMMON_CUSTOMER_INTERVAL_TIME*6,     
                            TimeUnit.SECONDS);
             
              Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
                            new Runnable(){
                                   publicvoid run()
                                   {
                                          Integer number = NumberMachine.getInstance().getExpressManager().generateNewManager();
                                          System.out.println(number+"號快速客戶等待服務!");
                                   }},
                            0,
                            Constants.COMMON_CUSTOMER_INTERVAL_TIME*2,     
                            TimeUnit.SECONDS);
       }
 
}</span>

程序結果如下:




---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity開發</a>、<a href="http://www.itheima.com"target="blank">.Net培訓</a>、期待與您交流! ----------------------

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章