看到张孝祥老师的Java面试题,原文见(http://www.it315.org/netclass/bank.html),忍不住手痒,用C#简单写了个
交通灯那题看得烦,回头再研究下,银行业务调度排号的现在比较多见,比较简单,估计月薪4K就可以了,实现如下:
首先看看需求:
拟实现银行业务调度系统逻辑,具体需求如下: 银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。 有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。 异步随机生成各种类型的客户,生成各类型用户的概率比例为: VIP客户:普通客户:快速客户 = 1 :6 :3。 客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。 各类型客户在其对应窗口按顺序依次办理业务。 当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。 随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。 不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
分析一下类的设计
对于业务窗口类,我可以认为是3种不同的银行员工(Employee),三种不同的银行员工都能够完成以下两件事情,从队列获取客户(GetCustomer),办理业务(DoBusiness)。
对于客户(Customer),三种不同客户暂时不对其特征进行任何抽象,仅认为所有的客户都有一个需要办理的业务时间(BusinessTime)而已,当然我们要利用CustomerFactory生产他们,并把他们放入到三个不同的队列中去。
最后,我们创建Bank类用于保存三个客户队列,为了简单起见,我们创建三个全局静态队列
现在业务逻辑就比较清晰了
Bank银行仅负责对三个客户队列进行维护
Customer仅仅是数据而已,保存在三个队列中
CustomerFactory是一个单独的工厂,用于随机生产不同的客户并把其加入到不同的队列中,这需要一个单独的线程
Employee是业务窗口,仅负责不停地查询三个队列,获取客户,然后针对客户办理业务,每个Employee实例都需要一个单独的线程
首先是客户类Customer
/// /// 客户 /// public class Customer { /// /// 客户名 /// public string Name { get; set; } /// /// 客户办理业务所需的时间 /// public TimeSpan BusinessTime { get; set; } }
然后是银行类:
public class Bank { /// /// 普通客户队列 /// readonly static Queue NormalCustomer; /// /// Vip客户队列 /// readonly static Queue VipCustomer; /// /// 快速客户队列 /// readonly static Queue QuickCustomer; static Bank() { //初始化三个队列 NormalCustomer = new Queue(); VipCustomer = new Queue(); QuickCustomer = new Queue(); } /// /// 从队列头获取普通客户,如果队列为空,返回null /// /// 获取的客户 public static Customer GetNormalCustomer() { lock (NormalCustomer) { if (NormalCustomer.Count > 0) return NormalCustomer.Dequeue(); return null; } } /// /// 增加普通客户到队列尾部 /// /// 要增加的客户 public static void AddNormalCustomer(Customer customer) { Info.AddInfo(customer, NormalCustomer); NormalCustomer.Enqueue(customer); } /// /// 从队列头获取VIP客户,如果队列为空,返回null /// /// 获取的客户 public static Customer GetVipCustomer() { lock (VipCustomer) { if (VipCustomer.Count > 0) return VipCustomer.Dequeue(); return null; } } /// /// 增加VIP客户到队列尾部 /// /// 要增加的客户 public static void AddVipCustomer(Customer customer) { Info.AddInfo(customer, VipCustomer); VipCustomer.Enqueue(customer); } /// /// 从队列头获取快速客户,如果队列为空,返回null /// /// 获取的客户 public static Customer GetQuickCustomer() { lock (QuickCustomer) { if (QuickCustomer.Count > 0) return QuickCustomer.Dequeue(); return null; } } /// /// 增加快速客户到队列尾部 /// /// 要增加的客户 public static void AddQuickCustomer(Customer customer) { Info.AddInfo(customer, QuickCustomer); QuickCustomer.Enqueue(customer); } }
为了防止队列异常和多线程冲突,简单地封装了对三个队列的获取和加入方法。
其中的Info类是输出信息用的,比较简单,一边日后重构用的
class Info { public static void AddInfo(Customer customer,ICollection queue) { Console.WriteLine("{0}:{1}加入等待队列,前面有{2}人等待。", DateTime.Now, customer.Name,queue.Count); } public static void AddBeginBusinessInfo(Employee employee, Customer customer) { Console.WriteLine("{0}:{1}开始为{2}办理业务。", DateTime.Now, employee.Name, customer.Name); } public static void AddEndBusinessInfo(Employee employee, Customer customer) { Console.WriteLine("{0}:{1}为{2}办理业务结束。", DateTime.Now, employee.Name, customer.Name); } }
利用一个简单工厂生产客户Customer
/// /// 工厂生成类 /// public class CustomerFactory { static TimeSpan totalWorkTime = new TimeSpan(0, 5,0);//总共工作5分钟 static TimeSpan interval = new TimeSpan(0, 0, 10);//每10秒产生一个客户 static TimeSpan maxTime=new TimeSpan(0,0,100);//客户最多办理业务100秒 static TimeSpan minTime=new TimeSpan(0,0,10);//客户最少办理业务10秒 static Random rnd = new Random();//随机种子 public static void CreateCustomer() { DateTime beginTime = DateTime.Now; DateTime endTime = DateTime.Now; Customer customer = null; int count=1;//已生成客户的数量 while ((endTime - beginTime) < totalWorkTime) { //随机生成办理业务的时间 TimeSpan time=new TimeSpan(0,0,rnd.Next (minTime.Seconds,maxTime.Seconds+1)); switch (rnd.Next(10)) { case 0: customer = new Customer { Name = "VIP客户" + count.ToString(), BusinessTime = time }; Bank.AddVipCustomer(customer); break; case 1: case 2: case 3: case 4: case 5: case 6: customer = new Customer { Name = "普通客户" + count.ToString(), BusinessTime = time }; Bank.AddNormalCustomer(customer); break; default: customer = new Customer { Name = "快速客户" + count.ToString(), BusinessTime = minTime }; Bank.AddQuickCustomer(customer); break; } count++; Thread.Sleep(interval); endTime = DateTime.Now; } } }
普通业务窗口
/// /// 普通员工,普通业务窗口 /// public class Employee { /// /// 员工名 /// public string Name { get; set; } protected Thread thread; public Employee() { thread = new Thread(new ThreadStart(DoBusiness)); thread.Start(); } /// /// 当前办理业务的客户 /// protected Customer currentCustomer; /// /// 获取下一个要处理的客户 /// protected virtual void GetCustomer() { currentCustomer = Bank.GetNormalCustomer(); } protected void DoBusiness() { //不停地工作 while (true) { GetCustomer(); if (currentCustomer != null)//为客户办理业务 { Info.AddBeginBusinessInfo(this, currentCustomer); Thread.Sleep(currentCustomer.BusinessTime); Info.AddEndBusinessInfo(this, currentCustomer); } } } /// /// 停止工作 /// public void Stop() { thread.Abort(); thread.Join(); } }
VIP窗口和快速窗口继承自普通窗口,仅重写GetCustomer方法即可
/// /// VIP业务窗口 /// public class VipEmployee:Employee { protected override void GetCustomer() { currentCustomer = Bank.GetVipCustomer();//获取VIP客户 if (currentCustomer == null) { //没有VIP客户的情况下,获取普通客户 currentCustomer = Bank.GetNormalCustomer(); } } } /// /// 快速业务窗口 /// public class QuickEmployee:Employee { protected override void GetCustomer() { currentCustomer = Bank.GetQuickCustomer(); } }
调用就很简单了
static void Main(string[] args) { List all = new List { new Employee { Name = "普通业务窗口1" }, new Employee { Name = "普通业务窗口2" }, new Employee { Name = "普通业务窗口3" }, new Employee { Name = "普通业务窗口4" }, new VipEmployee { Name = "VIP业务窗口" }, new QuickEmployee { Name = "快速业务窗口" } }; Thread thd = new Thread(new ThreadStart(CustomerFactory.CreateCustomer)); thd.Start(); }
以上Vs2008 framework3.5通过