BankQueue


银行项目设计思路:

(名词就提炼成对象,动词就提炼成功能方法)

1、所有设计的可能的对象:

                   窗口,客户,对应都是三种,快速,普通,vip;1:6:3;数据库;

 

2、各对象的功能方法:

                   窗口:业务办理时间设置;办理业务;

                   客户:排队;(像这种有固定规律的动态的增减情况,可以用集合;

 

3、最简化的整体中的对象有:核心对象是;窗口

 

4、每个对象可以定义的内容有哪些?

         窗口:设置办理业务的设置时间;

                     办理业务:

 

可以用代码体现的技术:

1、因为有三类业务,可以对窗口用接口或者父类提炼一下,三个业务只需要重新定义核心的业务分布即可;用父类,因为有一些方法不必要重新写;

或者单独把业务封装,作为参数传进去也行;

2、每个窗口的业务都是独立的,互补相干的,所以用多线程的方式;

3、因为是银行,肯定所有窗口的数据都是共享的;这里需要定义一个数据库,作为构造方法的参数传入到每个窗口;这是很重要的共享数据和资源的一种方式;

 

视频笔记:

 

1、要注意一点要注意,对象的考虑一定要涉及到的所有的有的对象;如这里的号码管理器,就容易忽视;

2、一定要严格分对象来设计,不可以把所有的对象的工作搅在一起;思路一定要清晰;

3、变量类型尽量面向父级或者接口,这样使用起来会更加灵活;

4、用枚举和switch语句结合来代替ifelse,可以提高效率;

         用switch语句使用枚举时,可以不需要写枚举的类名;

         用到的一些Constants常量 要单独定义一个类;

要有这个定义常量的意识;不能都用硬编码的阿拉伯数字,不好;

 

思路总结:先把整个工作流程每一个步骤都弄明白,然后从中抽取对象和方法并将他们归到某一个对象类;在针对该类进行针对性的细化设计;并且在归类的过程中彼此间的联系以及这个联系用什么技术体现要想明白;只有先把所有的设计细节想明白了,才能下笔写代码;

 

自己开始的思路是没有把业务逻辑搞明白,这里虽然是模拟的银行,但不是模拟的银行业务,而是模拟的外部的取号系统;是两个不同的业务逻辑;

 

 

 

 

一个完整的逻辑的设计:

 

1、首先所有的内都是通过主函数具体化的;所有的类也都仅仅是用于本项目的具体事物的一个抽象;并且最终通过主函数实例化并实现实际业务的逻辑关系;所有的实际业务都是在主函数下实现的,而其他的类不过是对业务的某一些特征进行封装;是业务的实现过程更有条理;更有顺序;是有章可循的;

 

2、这是一个取号系统,不是银行业务,不要把业务逻辑搞混了;

在这点上,做项目设计必须一定要把项目的系统定位弄明白,不然定位不明白基本是很难得到正确结果的,只可能错错得正,而且还要错的很巧;

所以项目设计要首先要明白三个:

系统定位(干嘛使的,非常具体);最小的整体感;核心对象;

 

3、所以这个项目系统的对象应该有:

(1)号码管理的机器,可以生成三类票,并且互不相干;生成号码与银行窗口没有关系;

(2)银行窗口;4普通,1快速,1vip;

(3)三类客户;可以定义成枚举,然后其他两个对象的客户根据这个来引用;

其实可以把号码管理机器统一到客户里面来;因为客户的生成本来就与窗口是没有关系的;

 

4、整个项目的设计流程就是抽象的流程;在整个业务演变的流程中进行抽象类,抽象方法;抽象完毕后,最后有条理的在主函数中进行实例化;将抽象的东西有条理的并按照彼此逻辑实现出来;

 

5、尽量有javabean的思路,只要是取值,设置等都调用成方法的形式,不要通过调用别的类的属性来取值,这是不专业的,专业的别的属性取值是要通过方法的,因为属性一般都是私有的;

而且按照javabean的思路,提供方法将比提供属性更安全,因为提供方法可以同步,提供属性访问无法操作同步,因为访问的人多了;参考自己做的第一份结果,总出现脚标越界;

 

6、根据项目设计三个要先明白的:

系统定位:取号系统;

最小的整体感:窗口,和客户;

核心的对象:窗口;

 

窗口:可以定义的内容:

窗口开启的方法,提供人性化的考虑,可能有暂停业务;或者重新设置办理业务类型;故引入参数设置;即每个窗口自定义一个号码;

办理业务的方法;取号的方法;

其实三类业务可以用一个接口或者抽象父类,然后分别实现即可;

 

客户:可以描述的内容;

三类客户的元素;

生成号码的方法;定义在构造函数,初始化就生成;

取得号码的方法;

自定义的toString方法;

 

7、自己做时出现了安全问题,很大原因在于自己总是想简化代码,其实在自己对流程思路还没有弄得非常明白时,可以先把该封装的,全部都封装,然后再进行简化,甚至可以把看不懂的都封装,只简化看得懂的,然后再去简化封装的代码;

当实在没有理清思路时,可以按流程一个对象就写一个类,然后写完之后,再抽象;

切记,思路清晰是第一位,简化代码时第二位的;在项目设计的时候;

 

8、避免多线程访问时出现安全问题,可以把共享的资源让他不共享,即在判断之前就取出来,之后再判断,不要在取的时候判断;

 

 

 


<think>我们正在讨论银行排队模拟中的单队列算法实现方案。根据引用内容,单队列模拟通常使用队列数据结构来实现基本操作,如创建空队列、检查空/满状态、入队(队尾添加)、出队(队首删除)、获取队列长度等。引用[1]提到队列类的基本设计,引用[2]和[3]则分别提供了多队列找最短队列和离散事件模拟的思路。但用户现在要求单队列,所以主要关注一个队列的模拟。单队列模拟的算法框架可以有两种:1.时间步进模拟:将时间划分为小的时间单位(如分钟),在每个时间步中检查事件(顾客到达、服务完成)。2.离散事件模拟:按事件发生的顺序处理,事件包括顾客到达和顾客离开(服务完成)。事件按发生时间排序,使用优先队列管理事件。由于引用[3]提到了离散事件模拟,而且这是一种更高效的方式,我们采用离散事件模拟来设计单队列算法。单队列离散事件模拟主要组成部分:-事件:包括到达事件(顾客到达)和离开事件(顾客完成服务离开)。每个事件包含发生时间、事件类型(到达/离开)以及可能的顾客信息。-事件队列:按发生时间排序的事件优先队列(最小堆)。-一个队列(即单队列)用于表示排队中的顾客。-服务窗口状态(空闲或繁忙)。注意,单队列单窗口情况下,每次只能服务一个顾客;单队列多窗口时,可以同时服务多个顾客。由于问题是单队列,但未说明窗口数,这里先考虑单窗口(最典型)。步骤:1.初始化:-创建事件队列(优先队列),初始化当前时间(例如0)。-创建用于排队的队列(我们称为等待队列,FIFO)。-设置服务窗口状态为空闲。-生成第一个顾客的到达事件,加入事件队列。2.循环处理事件队列,直到事件队列为空或者模拟结束条件(如达到最大模拟时间):-从事件队列中取出发生时间最早的事件。-如果事件是到达事件:设置当前时间为事件发生时间。然后,如果服务窗口空闲,则当前顾客立即开始接受服务,生成该顾客的离开事件(离开时间=当前时间+服务时长)并加入事件队列;同时将服务窗口状态置为忙。如果服务窗口忙,则将顾客加入等待队列(入队操作)。生成下一个顾客的到达事件(到达时间=当前时间+到达间隔)加入事件队列(除非已经到达顾客数达到预设值)。-如果事件是离开事件:设置当前时间为事件发生时间。服务窗口置为空闲。如果等待队列不为空,则从等待队列中出队一个顾客,该顾客立即开始服务,生成其离开事件;服务窗口再次置为忙。3.需要记录的信息:每个顾客的等待时间、总顾客数、平均等待时间等性能指标。根据引用[3]的提示,在离开事件中,删除队头后(即服务完成),如果等待队列还有顾客,则要立即生成下一个顾客的离开事件,其离开时间等于当前时间(即刚完成的离开事件发生时间)加上新顾客的服务时间。这一点非常重要。代码实现框架(伪代码):初始化事件队列eventQueue(优先队列,按发生时间排序)初始化等待队列waitQueue(普通FIFO队列)intcurrentTime=0;inttotalCustomer=0;//总顾客数inttotalWaitTime=0;//总等待时间窗口状态busy=false;//生成第一个到达事件eventQueue.push(Event{0,ARRIVAL,0});//假设0时刻第一个顾客到达,顾客编号0while(!eventQueue.empty()){Eventev=eventQueue.pop();currentTime=ev.occurTime;//推进当前时间if(ev.type==ARRIVAL){totalCustomer++;//生成下一个到达事件?(如果还有顾客需要到达)例如:nextArrivalTime=currentTime+到达间隔//这里假设到达间隔是随机的,比如指数分布,但也可以是指定分布。//注意:不能无限生成,可以设置最大顾客数或者最大时间if(!busy){//窗口空闲,立即服务busy=true;//随机生成该顾客的服务时长,然后生成离开事件intserviceTime=...;//随机服务时间eventQueue.push(Event{currentTime+serviceTime,DEPARTURE,ev.customerId});}else{//窗口忙,加入等待队列waitQueue.push(ev.customerId);//注意:这里也可以存储更多顾客信息}//生成下一个顾客的到达事件(假设还有下一个)intnextArrival=...;//下一个到达时间eventQueue.push(Event{nextArrival,ARRIVAL,totalCustomer});//注意顾客编号}elseif(ev.type==DEPARTURE){//离开事件:服务完成if(!waitQueue.empty()){//有顾客在等待,从队头取出一个顾客intnextCustomer=waitQueue.pop();//计算该顾客等待时间:当前时间-该顾客到达的时间?这里注意:我们在到达时加入队列,但队列中并没有存储到达时间//因此,需要改进:在顾客到达时,记录其到达时间,这样在开始服务时就能计算等待时间//所以,我们需要记录每个顾客的到达时间。可以设计一个顾客结构体,然后在事件中存储顾客信息(包含到达时间)或者单独存储。//假设我们有一个全局数组记录每个顾客的到达时间:arrivalTime[nextCustomer]=...?//在到达事件发生时,记录该顾客的到达时间。然后这里:intwaitTime=currentTime-arrivalTime[nextCustomer];totalWaitTime+=waitTime;//为该顾客生成离开事件intserviceTime=...;//该顾客的服务时间eventQueue.push(Event{currentTime+serviceTime,DEPARTURE,nextCustomer});}else{busy=false;//没有顾客在等待,窗口空闲}}}//计算平均等待时间:totalWaitTime/totalCustomer注意:上述代码中,关于顾客到达时间的记录需要单独管理。我们可以在生成到达事件时就创建顾客信息(包含到达时间),并保存到一个数组或映射中。在离开事件处理中,根据顾客编号来获取到达时间。改进:将事件类型定义好,顾客信息可以包含在事件中,但注意事件队列按时间排序。同时,在到达事件发生时,我们记录该顾客的到达时间(可以用一个数组customerArrivalTimes,索引为顾客编号)。另外,引用[3]中特别提到,在离开事件后立即为等待队列中的下一位顾客创建离开事件时,离开事件的发生时间等于当前事件发生时间(即刚结束服务的时间)加上新顾客的服务时间。这一点我们在上面的代码中已经体现。离散事件模拟需要全局的事件队列(优先队列)来管理事件,事件类型包括到达和离开。在模拟过程中,时间不是一步一步推进,而是跳跃到下一个事件发生的时间。由于用户需要实现方案,我们提供代码框架。这里我们用C++实现,参考引用[1]中的队列类,但是也可以使用STL的queue和priority_queue。完整离散事件模拟单队列单窗口的代码框架(C++)如下:注意:我们使用STL,需要包含头文件<queue>和<vector>。事件类型定义:事件类(Event)包含:occurTime(发生时间)、type(类型,0到达,1离开)、customerId(顾客编号)。注意:优先队列(事件队列)需要自定义比较函数(按occurTime从小到大)。另外,我们假设顾客到达时间间隔服从泊松分布(指数分布),服务时间也服从指数分布(实际中常用)。但也可以指定其他分布。代码示例:</think> ### 银行排队模拟:单队列算法实现方案 在银行排队模拟中,单队列算法是最基础的实现方式,所有顾客进入同一个队列排队,遵循"先进先出"(FIFO)原则。以下是基于队列数据结构的完整实现方案: #### **核心数据结构设计** ```cpp class BankQueue { private: int *items; // 存储队列元素的数组 int capacity; // 队列最大容量 int front; // 队首索引 int rear; // 队尾索引 int count; // 当前队列元素数量 public: // 构造函数(创建空队列) BankQueue(int size) { capacity = size; items = new int[size]; front = 0; rear = -1; count = 0; } // 检查队列是否为空 bool isEmpty() { return count == 0; } // 检查队列是否已满 bool isFull() { return count == capacity; } // 在队尾添加元素(入队) void enqueue(int customerID) { if (isFull()) throw "Queue full"; rear = (rear + 1) % capacity; items[rear] = customerID; count++; } // 在队首删除元素(出队) int dequeue() { if (isEmpty()) throw "Queue empty"; int customer = items[front]; front = (front + 1) % capacity; count--; return customer; } // 获取队列当前长度 int queueLength() { return count; } }; ``` #### **离散事件模拟流程** 基于引用[3]的离散事件模型: ```cpp void simulateSingleQueue() { // 初始化 BankQueue queue(100); // 创建容纳100人的队列 int currentTime = 0; int nextArrival = 0; // 下一个到达时间 int nextDeparture = -1;// 下一次离开时间 while (currentTime < SIMULATION_TIME) { // 处理到达事件 if (currentTime == nextArrival) { queue.enqueue(generateCustomerID()); nextArrival += generateInterval(); // 生成下个到达间隔 // 如果队列刚变非空,设置服务结束时间 if (queue.queueLength() == 1) { nextDeparture = currentTime + generateServiceTime(); } } // 处理离开事件[^3] if (currentTime == nextDeparture) { int customer = queue.dequeue(); logService(customer, currentTime); // 如果队列非空,立即开始服务下个客户 if (!queue.isEmpty()) { nextDeparture = currentTime + generateServiceTime(); } else { nextDeparture = -1; // 队列空时无离开事件 } } currentTime++; } } ``` #### **关键实现细节** 1. **时间推进机制** 采用**事件驱动**方式推进模拟时间: - 仅在有事件(到达/离开)发生时才推进时间 - 通过`nextArrival`和`nextDeparture`变量确定下一个事件时间点 2. **服务连续性保证** 当客户完成服务时,**立即从队列中取出下一个客户开始服务**(如引用[3]强调)[^3]: ```cpp if (!queue.isEmpty()) { nextDeparture = currentTime + generateServiceTime(); } ``` 3. **性能统计** 可扩展记录: ```cpp struct Statistics { int totalCustomers = 0; int totalWaitTime = 0; int maxWaitTime = 0; }; // 当客户开始服务时: int waitTime = currentTime - arrivalTime[customer]; stats.totalWaitTime += waitTime; stats.maxWaitTime = max(stats.maxWaitTime, waitTime); ``` #### **适用场景分析** 1. **传统窗口服务** 单一业务柜台(如:专用业务窗口) 2. **自动服务终端** 单一ATM机或自助服务设备队列 3. **流量稳定环境** 客户到达间隔和服务时间波动较小的情况
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值