黑马程序员—7k月薪面试题之银行业务调度系统 - Go语言中文社区

黑马程序员—7k月薪面试题之银行业务调度系统


------- android培训java培训、期待与您交流! ----------

 

 

银行业务调度系统需求:

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

 

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

 

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

 

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

 

        VIP客户:普通客户:快速客户  =  16 3

 

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

 

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

 

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

 

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

 

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

 

 

 

思路:

银行的每一个客户是根据取号机来产生的一个号码来表示的。为什么这么说呢?因为假如一个人进了银行,但是他没有在取号机

  上取号码,那么,他是不算银行的客户的。所以要有一个号码管理器对象,不断产生号码,就相当于不断的随机的在产生客户一样。

 *

  而这个银行业务中有3类客户,他们的号码顺序是独立编排的,本应当有三个号码管理器对象,但是,根据银行的实际情况,银行一般只有一个

  取号码的机器,这个机器它能够独立的生成每个客户类型的独立的编排顺序,所以,这3个号码管理器对象应该由一个号码机器对象进行管理。

  号码机器这个对象在银行中一般只有一个,所以,我们本程序中也只能有一个号码机器的对象,所以我们用单例模式设计一个号码机器对象。

  银行的每个窗口是怎么叫号的呢?是根据取对应的号码管理器,就是服务窗口每次会通过号码管理器来获取要服务的号码。

 *

  为了方便于理解,我们可以画一个类图来帮助我们更好的理解。

  设计一个NumberMachine类,它管理三个类型的NumberManager,每个NumberManager都有产生新的号码generateNewNumber()

  方法为客户产生新的号码,和fetchServiceNumber()获取号码方法给窗口ServiceWindow,让窗口知道该为几号客户服务。NumberMachine里有getCommonManager()

  获取普通用户管理器方法。getVIPManager()返回VIP用户的管理器方法。getExpressManager返回快速用户的管理器方法。

 NumberMachine是单例模式,所以里面提供静态方法,getInstance()

 

面向对象的分析与设计:

有三种对应类型的客户:VIP客户,普通客户,快速客户,异步随机生成各种类型的客户,各类型客户在其对应窗口按顺序依次办理业务

首先每一个客户其实就是由银行的一个取号机器产生号码的方式来表示的。所以,我想到要有一个号码管理器对象,让这个对象不断地产生号码,就等于随机生成了客户。

由于有三类客户,每类客户的号码编排都是完全独立的,所以,我想到本系统一共要产生三个号码管理器对象,各自管理一类用户的排队号码。这三个号码管理器对象统一由一个号码机器进行管理,这个号码机器在整个系统中始终只能有一个,所以,它要被设计成单例。

各类型客户在其对应窗口按顺序依次办理业务,准确地说,应该是窗口依次叫号。

各个窗口怎么知道该叫哪一个号了呢?它一定是问的相应的号码管理器,即服务窗口每次找号码管理器获取当前要被服务的号码。

 

 

 

类的编写:

NumberManager

定义一个用于存储上一个客户号码的成员变量和用于存储所有等待服务的客户号码的队列集合。

定义一个产生新号码的方法和获取马上要为之服务的号码的方法,这两个方法被不同的线程操作了相同的数据,所以,要进行同步。

 

代码体现:

package com.itheima.bank;

/*	经验小提示:
 * 	尽量面向接口编程,这样会更加灵活,更专业一点。如果想要实现的功能父类或者接口能完成,那么变量的类型尽量用父类或是接口。
 * */

import java.util.ArrayList;
import java.util.List;

//客户号码管理器类
public class NumberManager {
	//定义上一个号码属性
	private int lastNumber = 1;
	//创建队列集合,用于存放服务号码,每次取出时只能取出0角标元素
	private List<Integer> queueNumber = new ArrayList<Integer>();
	//产生新的用户号码方法
	public synchronized Integer generateNewNumber() {
		queueNumber.add(lastNumber);
		return lastNumber++;
	}
	//返回应该要服务的号码给服务窗口
	public synchronized Integer fetchServiceNumber() {
		Integer number = null;
		//判断集合不为空就取出0角标的号码
		if (queueNumber.size() > 0) {
			number = queueNumber.remove(0);
		}
		return number;
	}
}


 

 

NumberMachine

定义三个成员变量分别指向三个NumberManager对象,分别表示普通、快速和VIP客户的号码管理器,定义三个对应的方法来返回这三个NumberManager对象。

NumberMachine类设计成单例。

 

代码体现:

package com.itheima.bank;

//号码机器类
public class NumberMachine {
	//创建一个私有的普通客户号码的管理器对象
	private NumberManager commonManager = new NumberManager();
	//创建一个私有的VIP客户的号码管理器对象
	private NumberManager vipManager = new NumberManager();
	//创建一个私有的快速客户的号码管理器对象
	private NumberManager expressManager = new NumberManager();
	
	//提供公有的返回普通客户号码的管理器对象的getCommonManager()方法
	public NumberManager getCommonManager() {
		return commonManager;
	}
	//提供公有的返回VIP客户的号码管理器对象的getVipManager()方法
	public NumberManager getVipManager() {
		return vipManager;
	}
	//提供公有的返回快速客户的号码管理器对象的getExpressManager()方法
	public NumberManager getExpressManager() {
		return expressManager;
	}
	
	//设计NumberMachine类对象的单例模式
	//私有化构造函数
	private NumberMachine() {}
	//创建私有的静态的一个NumberMachine类的一个对象
	private final static NumberMachine numberMachine = new NumberMachine();
	//提供公有的静态的返回NumberMachine类对象的实例对象的getInstance()方法
	public static NumberMachine getInstance() {
		return numberMachine;
	}
}


 

 

CustomerType枚举类

系统中有三种类型的客户,所以用定义一个枚举类,其中定义三个成员分别表示三种类型的客户。

重写toString方法,返回类型的中文名称。这是在后面编码时重构出来的,刚开始不用考虑。

代码体现:

package com.itheima.bank;

//客户类型枚举类
public enum CustomerType {
	//三个客户类型,普通客户,快速客户,VIP客户
	COMMON,EXPRESS,VIP;
	
	//重写toString方法,根据调用者符客户类型的不同返回不同类型客户的中文表现形式
	public String toString() {
		switch (this) {
			case COMMON:
				return "普通";
			case EXPRESS:
				return "快速";
			case VIP:
				return name();
		}
		return null;
	}
}


 

 

ServiceWindow

定义一个start方法,内部启动一个线程,根据服务窗口的类别分别循环调用三个不同的方法。

定义三个方法分别对三种客户进行服务,为了观察运行效果,应详细打印出其中的细节信息。

代码体现:

package com.itheima.bank;

/*	开发经验提示:要学习新技术,新技术往往能解决很多开发过程中的大问题。
 * */

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

//服务窗口类
public class ServiceWindow {
	//创建一个客户类型,初始化类型为普通客户
	private CustomerType custType = CustomerType.COMMON;
	private int windowId = 1;
	//setCustType()方法,设置客户类型
	public void setCustType(CustomerType custType) {
		this.custType = custType;
	}
	//setWindowId()方法,设置服务窗口初始化值
	public void setWindowId(int windowId) {
		this.windowId = windowId;
	}
	
	//start()方法
	public void start() {
		//创建一个单独的线程池
		Executors.newSingleThreadExecutor().execute(new Runnable() {
			//线程要执行的操作
			public void run() {
				while (true) {
//					if (custType == CustomerType.COMMON)
//						NumberMachine.getInstance().getCommonManager();
//					else if (custType == CustomerType.EXPRESS)
//						NumberMachine.getInstance().getExpressManager();
//					else
//						NumberMachine.getInstance().getVipManager();
				
					//switch效率比if{} else if{}效率要高,而且还可以省略前缀,所以这里使用switch
					switch (custType) {
				
						//如果为普通客户
						case COMMON:
							//调用普通用户服务方法
							commonService();	
							break;
						
						//如果为快速用户
						case EXPRESS:
							//调用快速服务的方法
							expressService();
							break;
						
						//如果为VIP用户
						case VIP:
							//调用vip客户服务方法
							vipService();
							break;
					}
				}
			}
		});
	}
	
	//普通用户服务方法
	private void commonService() {
		//封装服务窗口号和客户类型
		String windowName = windowId + "号" + custType + "窗口";
		//获取普通客户服务号码
		Integer commonNumber = NumberMachine.getInstance().getCommonManager().fetchServiceNumber();
		//打印提示信息
		System.out.println(windowName+"正在获取服务号码");
		//判断是否有需要服务的普通客户号码
		if (commonNumber != null) {
			//打印提示信息
			System.out.println(windowName+"获取到"+commonNumber+"号的普通客户,正在为他服务!");
			//记录服务开始时间
			long startTime = System.currentTimeMillis();
			//得到一个随机值范围 ,10000-1000=9000毫秒
			int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
			//生成随机服务时间 0-8999毫秒+1+1000=1001-10000毫秒之间
			int serviceTime = new Random().nextInt(maxRandom) + 1 + Constants.MIN_SERVICE_TIME;
			try {
				//线程睡眠过服务时间再激活
				Thread.sleep(serviceTime);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//记录服务消耗时间
			long spendTime = System.currentTimeMillis() - startTime;
			//打印服务的详细过程
			System.out.println(windowName + "为第"+commonNumber+"号普通客户服务消耗的时间" + (spendTime/1000) + "秒");
		}
		else {
			//没有获取到普通客户的服务号码打印提示信息
			System.out.println(windowName+"没有获取到要服务的客户,工作人员休息一秒后再工作!");
			try {
				//没有获取到任务时线程睡眠1秒后再激活
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	//快速服务方法
	private void expressService() {
		//封装服务窗口号和客户类型
		String windowName = windowId + "号" + custType + "窗口";
		//获取快速用户的服务号码
		Integer expressNumber = NumberMachine.getInstance().getExpressManager().fetchServiceNumber();
		//打印提示信息
		System.out.println(windowName+"正在获取服务号码");
		//判断是否有需要服务的快速客户号码
		if (expressNumber != null) {
			//打印提示信息
			System.out.println(windowName+"获取到"+expressNumber+"号的快速客户,正在为他服务!");
			//记录服务开始时间
			long startTime = System.currentTimeMillis();
			try {
				//线程睡眠过服务时间再激活
				Thread.sleep(Constants.MIN_SERVICE_TIME + 1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//记录服务消耗时间
			long spendTime = System.currentTimeMillis() - startTime;
			//打印服务的详细过程
			System.out.println(windowName + "为第"+expressNumber+"号快速客户服务消耗的时间为" + (spendTime/1000) + "秒");
		}
		else {
			//没有获取到快速客户的服务号码打印提示信息
			System.out.println(windowName+"没有获取到要服务的客户!");
			commonService();
		}
	}
	
	//vip客户服务方法
	private void vipService() {
		//封装服务窗口号和客户类型
		String windowName = windowId + "号" + custType + "窗口";
		//那么获取VIP用户的服务号码
		Integer vipNumber = NumberMachine.getInstance().getVipManager().fetchServiceNumber();
		//打印提示信息
		System.out.println(windowName+"正在获取服务号码");
		//判断是否有需要服务的VIP客户号码
		if (vipNumber != null) {
			//打印提示信息
			System.out.println(windowName+"获取到"+vipNumber+"号的VIP客户,正在为他服务!");
			//记录服务开始时间
			long startTime = System.currentTimeMillis();
			//得到一个随机值范围 ,10000-1000=9000毫秒
			int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
			//生成随机服务时间 0-8999毫秒+1+1000=1001-10000毫秒之间
			int serviceTime = new Random().nextInt(maxRandom) + 1 + Constants.MIN_SERVICE_TIME;
			try {
				//线程睡眠过服务时间再激活
				Thread.sleep(serviceTime);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//记录服务消耗时间
			long spendTime = System.currentTimeMillis() - startTime;
			//打印服务的详细过程
			System.out.println(windowName + "为第"+vipNumber+"号VIP客户服务消耗的时间为" + (spendTime/1000) + "秒");
		}
		else {
			//没有获取到VIP客户的服务号码打印提示信息
			System.out.println(windowName+"没有获取到要服务的客户!");
			commonService();
		}
	}
}


 

 

Constants

定义三个常量:MAX_SERVICE_TIMEMIN_SERVICE_TIMECOMMON_CUSTOMER_INTERVAL_TIME

代码体现:

package com.itheima.bank;

//常量类
public class Constants {
	//最长服务时间
	public final static int MAX_SERVICE_TIME = 10000;
	//最短服务时间
	public final static int MIN_SERVICE_TIME = 1000;
	//普通客户取号间隔时间
	public final static int COMMON_CUSTOMER_INTERVAL_TIME = 1;
}


 

 

MainClass

for循环创建出4个普通窗口,再创建出1个快速窗口和一个VIP窗口。

接着再创建三个定时器,分别定时去创建新的普通客户号码、新的快速客户号码、新的VIP客户号码。

代码体现:

package com.itheima.bank;

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

//程序主类
public class MainClass {
	public static void main(String[] args) {
		//循环创建5个普通服务窗口
		for (int i = 1; i <= 4; i++) {
			//创建普通服务窗口
			ServiceWindow commonWindow = new ServiceWindow();
			//设置普通服务窗口号
			commonWindow.setWindowId(i);
			//开启线程
			commonWindow.start();
		}
		
		//创建一个快速服务窗口
		ServiceWindow expressWindow = new ServiceWindow();
		//设置快速服务窗口为5号窗口
		expressWindow.setWindowId(5);
		//设置快速服务窗口要服务的对象为快速客户类型
		expressWindow.setCustType(CustomerType.EXPRESS);
		//开启线程
		expressWindow.start();
		
		//创建一个vip窗口
		ServiceWindow vipWindow = new ServiceWindow();
		//设置vip窗口号码为6
		vipWindow.setWindowId(6);
		//设置Vip窗口服务对象为VIP客户类型
		vipWindow.setCustType(CustomerType.VIP);
		//开启线程 
		vipWindow.start();
		
		
		//定时器,定时固定频率的执行某一操作
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
				//定时要执行的代码
				new Runnable() {
					//线程要执行的操作
					public void run() {
						//普通客户取号
						int commonNumber = NumberMachine.getInstance().getCommonManager().generateNewNumber();
						System.out.println(commonNumber+"号普通用户取到号码,等待服务");
					}
				},
				0,//延迟0秒执行
				Constants.COMMON_CUSTOMER_INTERVAL_TIME,//每隔1秒来一个普通客户取号
				TimeUnit.SECONDS
			);
		
		//定时器,定时固定频率的执行某一操作
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
				//定时要执行的代码
				new Runnable() {
					//线程要执行的操作
					public void run() {
						//VIP客户取号
						int vipNumber = NumberMachine.getInstance().getVipManager().generateNewNumber();
						System.out.println(vipNumber+"号VIP用户取到号码,等待服务");
					}
				},
				0,//延迟0秒执行
				Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6,//每隔6秒来一个VIP客户取号
				TimeUnit.SECONDS
			);
		
		//定时器,定时固定频率的执行某一操作
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
				//定时要执行的代码
				new Runnable() {
					//线程要执行的操作
					public void run() {
						//快速客户取号
						int expressNumber = NumberMachine.getInstance().getExpressManager().generateNewNumber();
						System.out.println(expressNumber+"号快速用户取到号码,等待服务");
					}
				},
				0,//延迟0秒就取号
				Constants.COMMON_CUSTOMER_INTERVAL_TIME * 2,//每隔3秒就来一个快速客户取号
				TimeUnit.SECONDS
			);
				
	}
}

 

程序运行截取的部分结果:

5号快速窗口获取到20号的快速客户,正在为他服务!

1号普通窗口为第29号普通客户服务消耗的时间5

1号普通窗口正在获取服务号码

1号普通窗口获取到33号的普通客户,正在为他服务!

41号普通用户取到号码,等待服务

21号快速用户取到号码,等待服务

5号快速窗口为第20号快速客户服务消耗的时间为1

5号快速窗口正在获取服务号码

5号快速窗口获取到21号的快速客户,正在为他服务!

6VIP窗口为第32号普通客户服务消耗的时间1

6VIP窗口正在获取服务号码

6VIP窗口没有获取到要服务的客户!

6VIP窗口正在获取服务号码

6VIP窗口获取到34号的普通客户,正在为他服务!

3号普通窗口为第28号普通客户服务消耗的时间8

3号普通窗口正在获取服务号码

3号普通窗口获取到35号的普通客户,正在为他服务!

42号普通用户取到号码,等待服务

5号快速窗口为第21号快速客户服务消耗的时间为1

5号快速窗口正在获取服务号码

5号快速窗口没有获取到要服务的客户!

5号快速窗口正在获取服务号码

5号快速窗口获取到36号的普通客户,正在为他服务!

 

 

------- android培训java培训、期待与您交流! ----------
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/caihongxiuyujay/article/details/23858787
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-05-29 20:21:43
  • 阅读 ( 1162 )
  • 分类:面试题

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢