面向对象的关键:
你不能首先考虑这个程序需要实现什么,你必须要首先思考这个程序要模拟什么
也就是说,你必须要先通过在实际生活中的思维方式,把整个业务流程模拟出来
然后再对整个业务流程进行抽象。
需求如下:
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方式展现程序运行结果。
面对具体需求时,首先要理清面向的对象有哪些,然后再进行编程的思路分析。
问题分析:
1. 有三种对应类型的客户:VIP客户,普通客户,快速客户 ,异步随机生成各种类型的客户,各类客户在对应窗口按顺序依次办理业务 。
有三类客户,每类客户取号都是完全独立的,取号机系统内置了三种类型的号码生成器对象,各自生成一类用户的排队号码。
这三种类型的号码生成器对象统一由一个取号机进行管理,这个取号机在整个系统中始终只能有一个,所以,它要被设计成单例。
2. 各类型客户等待窗口依次叫号。
各个窗口通过取号机的三种类型的号码生成器对象取得当前要服务客户的号码。
能否挖掘出业务中隐含的对象信息,是解决模拟的关键。
综上所述,需要三个主类
1、需要有一个号码生成类,负责异步生成号码并为窗口提供下一个需要服务的号码,涉及到多线程,建号取号方法需加同步。
1 package cn.itcast.interview2; 2 import java.util.ArrayList; 3 import java.util.List; 4 5 public class NumberManager { 6 //定义私有成员变量lastNumber,表示队列中最后一个数 7 private int lastNumber = 1; 8 //定义list集合queueNumber用于存放号码 9 public List<Integer> queueNumber= new ArrayList<Integer>(); 10 11 //定义方法generateNewNumber()用于产生号码 12 //由于产生号码和获取号码必须保证同步,因此加入同步代码块 13 public synchronized Integer generateNewNumber(){ 14 queueNumber.add(++lastNumber); 15 return lastNumber; 16 } 17 18 //定义方法fetchNumber()用于获取排列在最初的号码 19 //由于产生号码和获取号码必须保证同步,因此加入同步代码块 20 public synchronized Integer fetchNumber(){ 21 22 if (queueNumber.size()>0){ 23 return (Integer)queueNumber.remove(0); 24 }else{ 25 return null; 26 } 27 } 28 29 }
2、需要有一个取号机类,该取号机只能通过单例模式来产生,该号码机内置三种类型的号码生成器对象。
1 package cn.itcast.interview2; 2 3 public class NumberMechine { 4 5 /*号码机是用于给不同柜台产生号码用的,由于号码机的唯一性,这里考虑使用单例设计模式*/ 6 7 //首先,构造函数私有化 8 private NumberMechine(){} 9 10 //其次,创建本类对象 11 private static NumberMechine instance = new NumberMechine(); 12 13 //最后,提供获得实例的方法 14 public static NumberMechine getInstance(){ 15 return instance; 16 } 17 18 //定义三个窗口的对象,并提供get方法 19 private NumberManager commonManager = new NumberManager(); 20 private NumberManager expressManager = new NumberManager(); 21 private NumberManager vipManager = new NumberManager(); 22 23 public NumberManager getCommonManager() { 24 return commonManager; 25 } 26 public NumberManager getExpressManager() { 27 return expressManager; 28 } 29 public NumberManager getVipManager() { 30 return vipManager; 31 } 32 33 }
3、需要有一个服务窗口类,它可以从号码生成器中获取下一个需要服务的号码,并且对3种不同的客户进行服务
1 package cn.itcast.interview2; 2 3 import java.util.Random; 4 import java.util.concurrent.Executors; 5 import java.util.logging.Logger; 6 7 public class ServiceWindow { 8 private static Logger logger = Logger.getLogger("cn.itcast.interview2"); 9 private CustomerType type = CustomerType.COMMON; 10 private int windowId = 1;//默认从1号窗口开始提供服务 11 12 public void setType(CustomerType type){ 13 this.type = type; 14 } 15 16 public CustomerType getType() { 17 return type; 18 } 19 20 public void setNumber(int number) { 21 this.number = number; 22 } 23 24 public void start(){ 25 Executors.newSingleThreadExecutor().execute( 26 new Runnable(){ 27 public void run(){ 28 while(true){ 29 switch(type){ 30 case COMMON: 31 commonServer(); 32 break; 33 case EXPRESS: 34 expressServer(); 35 break; 36 case VIP: 37 vipServer(); 38 break; 39 } 40 } 41 } 42 }); 43 } 44 45 46 47 private void commonServer() { 48 String windowName = "第"+windowId+"号"+type+"窗口";//窗口名称 49 System.out.println(windowName+"为您服务,开始获取普通任务!");//窗口动态信息 50 Integer serviceNumber = NumberMechine.getInstance().getCommonManager().fetchNumber(); 51 if (serviceNumber!=null){ 52 System.out.println(windowName + "开始为第"+serviceNumber+"号客户服务"); 53 int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME; 54 int serviceTime = new Random().nextInt(maxRandom) + 1 + Constants.MIN_SERVICE_TIME; 55 try { 56 Thread.sleep(serviceTime); 57 } catch (InterruptedException e) { 58 // TODO 自动生成的 catch 块 59 e.printStackTrace(); 60 } 61 System.out.println(windowName + "完成为第" + serviceNumber + 62 "号普通客户服务,总共耗时" + serviceTime/1000 + "秒"); 63 }else{ 64 System.out.println("没有取到普通任务,一秒后重新取任务!"); 65 try { 66 Thread.sleep(1000); 67 } catch (InterruptedException e) { 68 // TODO 自动生成的 catch 块 69 e.printStackTrace(); 70 } 71 } 72 73 74 } 75 76 protected void expressServer() { 77 String windowName = "第"+windowId+"号"+type+"窗口"; 78 System.out.println(windowName+"为您服务,开始获取快速任务!"); 79 Integer serviceNumber = NumberMechine.getInstance().getExpressManager().fetchNumber(); 80 if (serviceNumber!=null){ 81 System.out.println(windowName + "开始为第"+serviceNumber+"号客户服务"); 82 83 int serviceTime =Constants.MIN_SERVICE_TIME; 84 try { 85 Thread.sleep(serviceTime); 86 } catch (InterruptedException e) { 87 // TODO 自动生成的 catch 块 88 e.printStackTrace(); 89 } 90 System.out.println(windowName + "完成为第" + serviceNumber + 91 "号快速客户服务,总共耗时" + serviceTime/1000 + "秒"); 92 }else{ 93 System.out.println("没有取到快速任务,正在尝试获取普通任务!"); 94 commonServer(); 95 } 96 97 } 98 protected void vipServer() { 99 String windowName = "第"+windowId+"号"+type+"窗口"; 100 System.out.println(windowName+"为您服务,开始获取VIP任务!"); 101 Integer serviceNumber = NumberMechine.getInstance().getVipManager().fetchNumber(); 102 if (serviceNumber!=null){ 103 System.out.println(windowName + "开始为第"+serviceNumber+"号客户服务"); 104 int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME; 105 int serviceTime = new Random().nextInt(maxRandom) + 1 + Constants.MIN_SERVICE_TIME; 106 try { 107 Thread.sleep(serviceTime); 108 } catch (InterruptedException e) { 109 // TODO 自动生成的 catch 块 110 e.printStackTrace(); 111 } 112 System.out.println(windowName + "完成为第" + serviceNumber + 113 "号VIP客户服务,总共耗时" + serviceTime/1000 + "秒"); 114 }else{ 115 System.out.println("没有取到VIP任务,正在尝试获取普通任务!"); 116 commonServer(); 117 } 118 119 } 120 121 }
需要两个支类:
1.客户的类型是固定的,设计成枚举类来使用
1 package cn.itcast.interview2; 2 3 public enum CustomerType { 4 /*三种顾客类型分别是,普通客户,快速客户,VIP客户*/ 5 COMMON, 6 EXPRESS, 7 VIP; 8 9 public String toString(){ 10 String name = null; 11 switch(this){ 12 13 case COMMON: 14 name = "普通"; 15 break; 16 case EXPRESS: 17 name = "快速"; 18 break; 19 case VIP: 20 name = "VIP"; 21 break; 22 23 } 24 return name; 25 26 } 27 28 }
2.常量时间
1 package cn.itcast.interview2; 2 3 public class Constants { 4 public static int MAX_SERVICE_TIME = 10000; //10秒! 5 public static int MIN_SERVICE_TIME = 1000; //1秒! 6 7 /*每个普通窗口服务一个客户的平均时间为5秒,一共有4个这样的窗口,也就是说银行的所有普通窗口合起来 8 * 平均1.25秒内可以服务完一个普通客户,再加上快速窗口和VIP窗口也可以服务普通客户,所以, 9 * 1秒钟产生一个普通客户比较合理,*/ 10 public static int COMMON_CUSTOMER_INTERVAL_TIME = 1; 11 }
main
1 package cn.itcast.interview2; 2 3 import java.util.concurrent.Executors; 4 import java.util.concurrent.TimeUnit; 5 6 public class MainClass { 7 8 public static void main(String[] args) { 9 //产生4个普通窗口 10 for (int i = 0; i < 5; i++){ 11 ServiceWindow window = new ServiceWindow(); 12 window.setNumber(i); 13 window.start(); 14 } 15 16 //产生一个快速窗口 17 ServiceWindow expressWindow = new ServiceWindow(); 18 expressWindow.setType(CustomerType.EXPRESS); 19 expressWindow.start(); 20 21 //产生一个vip窗口 22 ServiceWindow vipWindow = new ServiceWindow(); 23 vipWindow.setType(CustomerType.VIP); 24 vipWindow.start(); 25 26 //普通客户拿号 27 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( 28 new Runnable(){ 29 public void run(){ 30 Integer serviceNumber = 31 NumberMechine.getInstance().getCommonManager().generateNewNumber(); 32 System.out.println("第" + serviceNumber + "号普通客户正在等待服务!"); 33 } 34 }, 35 0, 36 Constants.COMMON_CUSTOMER_INTERVAL_TIME, 37 TimeUnit.SECONDS 38 ); 39 40 //快速客户拿号 41 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( 42 new Runnable(){ 43 public void run(){ 44 Integer serviceNumber = 45 NumberMechine.getInstance().getExpressManager().generateNewNumber(); 46 System.out.println("第" + serviceNumber + "号快速客户正在等待服务!"); 47 } 48 }, 49 0, 50 Constants.COMMON_CUSTOMER_INTERVAL_TIME*3, 51 TimeUnit.SECONDS 52 ); 53 54 //vip客户拿号 55 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( 56 new Runnable(){ 57 public void run(){ 58 Integer serviceNumber = 59 NumberMechine.getInstance().getVipManager().generateNewNumber(); 60 System.out.println("第" + serviceNumber + "号vip客户正在等待服务!"); 61 } 62 }, 63 0, 64 Constants.COMMON_CUSTOMER_INTERVAL_TIME*6, 65 TimeUnit.SECONDS 66 ); 67 68 } 69 70 }