这即将是我人生中当中的第一篇blog,下面就由我来开始吧!
目录
题目集5主要包含了正则表达式和电梯1.0
题目集6主要包含了类的设计和电梯2.0
题目集7也是类的设计和电梯3.0
这三次题目集实现了电梯的迭代性设计。
1.首先,就是类的设计。在设计类的过程中,要遵守SRP原则,也就是单一职责原则。一个类只有一个引起它变化的原因,一个类只负责一项职责,可以增加类的稳定性和可维护性。在销售步枪问题中,设计了枪管,枪托,枪机和销售订单类。
枪管,枪托,枪机都有价格的私有属性,订单销售类负责各个的数量和计算佣金。
2.同时在类的设计过程中也要遵守迪米特法则(LOD)。即不要和陌生人讲话,只和你的朋友通信,最少知识原则。以汽车风挡玻璃雨刷问题为例,我设计了控制杆类、刻度盘类、雨刷类、控制类和和计算速度类。控制杆主要负责控制杆升档和控制杆降档;刻度盘类主要负责刻度盘升档和刻度盘降档;雨刷类主要负责设置雨刷的速度;控制类主要负责控制雨刷,升档和降档;计算速度类主要负责计算雨刷的速度。Agent类是符合迪米特法则的,Lever,Dial,Brush都是Agent的直接朋友。
3.下面来到了单部电梯调度程序。单部电梯调度程序采用了LOOK算法。即方向优先,优先处理同方向上的请求,然后再处理不同方向上的请求。在刚刚看到这题的时候,没有真正的理解题意,就开始写代码了,这是一个非常错误的行为。我们应真正地理解了题意之后,思考之后,再去写代码。
(1)在接受电梯数据的时候采用了split分割字符串,然后加入到数组中。用一维数组存储内部请求,用二维数组存储外部请求,如果是UP,则第二个元素记录为1;如果是DOWN,则第二个元素记录为-1;在刚刚设计类的时候,没有遵守单一职责原则,类图如下。一个电梯完成了太多的职责,例如判断下一次要去的楼层,增删数组,是否开门,判断方向。我们可以将电梯里面的函数拆分为多个类。在设置电梯的方向时,我一般喜欢使用1代表电梯向上,用-1代表电梯向下。
下面这一部分就是我算法的核心。
如果外部数组为空但内部数组不为空,那直接返回内部数组的首元素。如果外部数组不为空但内部数组为空,那直接返回外部数组的首元素。如果内部数组和外部数组都不为空的话,根据两个数组首元素减去当前楼层的正负号判断方向,如果都为方向相同并且符号也相同,那么根据距离的大小决定返回哪一个;如果方向相反的话,在判断内部数组减去当前楼层的正负是否等于电梯当前的运行方向,如果等于则返回内部数组的首元素,否则返回外部数组的首元素。如果方向相反但是都在当前楼层之上,返回内部数组首元素。如果外部数组首元素的方向等于电梯当前的运行方向,那么返回外部数组的首元素,否则返回内部数组的首元素。这段代码虽然通过了测试,但还是会存在一些小问题(后面会讲到)。但随着电梯的迭代,问题也会随之解决。在输出currentFloor时采用了while循环,每输出一层判断一次。判断是否开门时,看当前楼层是为等于数组首元素,如果等于则开门,否则不开门。
(2)电梯的2.0版本。在原来的基础上增加了请求楼层有误的情况和处理连续相同请求的能力(如果相同则跳过)。采用了LinkedList来处理用户的请求。当我把第一次的代码进行修改与分类后,相较与第一次,这次设计的类的职责也跟单一,分工更明确。进行提交,但发现只过了第二个测试点,没有通过第一个测试点。于是我自己做了几个测试样例去试,却发现了我的算法存在一些逻辑问题。较比于第一次电梯题目,第二次电梯题目的算法复杂度比第一次的高。我处理的只是某些情况,而除了这些情况外,还有其他情况没有处理,没有把这个问题抽象出来,只涉及到了一些方面,前面的一些问题于是暴露了出来。在原来的基础上我有把算法改了改,以电梯运行的方向为导向进行处理。
如果电梯的运行方向为向上,此时如果外部请求的方向为向下,再根据外部请求和内部请求与电梯当前的楼层的相对位置进行判断,如果内部队列在当前楼层之上,则返回内部队列的首元素;如果都在当前楼层之下,则比较他们距离当前楼层的距离,谁近就返回谁。如果方向相同,如果内部队列和外部队列都在当前楼层之上,那么谁离当前楼层近就返回谁,但是如果内部队列的首元素等于外部队列的首元素,那么就返回外部队列的源楼层;如果外部楼层的源楼层等于当前楼层并且目标楼层大于当前楼层,则返回源楼层;如果内部楼层大于当前楼层,返回内部楼层的目标楼层;如果外部楼层的源楼层大于当前楼层,返回源楼层;如果内部楼层大于外部楼层,返回外部楼层的源楼层,否则返回内部楼层。如果电梯的运行方向为向下,具有对称性,处理过程也是如此。在此过程中,我们可以发现处理方向请求的代码具有对称性。
在处理开关门的时候也遇到了一些问题,例如,虽然电梯的当前楼层等于队首,但是由于方向不同,不该开门的时候也开门了。在这里我用了flag作为标志避免的这种情况的发生。如果相同则flag为true,等都判断完了,再根据flag的真值判断是否需要开关门。
(3)电梯的3.0版本。在原来的基础上题意又做了新的变化,作为一名程序员,我们要做到的就是拥抱变化,使程序具有扩展性。此次的算法复杂度又比上一次的算法复杂度高。外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的层>,处理完外部层后要将请求目的层加入到内部队列的队首。在外部队列中,通过请求目的层减去请求源楼层的正负号规定外部队列请求的正负号,如果为正的话,就相当于第二次电梯中的UP,如果为负的话,就相当于DOWN。在编码过程中,要记得处理完外部请求后,要将外部请求的目标楼层加入到内部请求的队尾,我有几次就忘记了。在开门的那里,也出现了一些错误。忽略了外部队列的请求源楼层和内部队列的请求目的层相等的情况,导致有时候会多次开门。
在算法中,沿用第二次的算法,现实却给了我沉重一击,还是有漏洞。核心算法那一部分还是漏了一点东西,没有全部通过测试点。比如我没有处理外部队列中请求源楼层和内部队列中楼层相等的情况,导致电梯会运行到0楼。在加上这一部分之后就可以了。
1.在写编程题之后,一定要把题目看清楚,把题目理解透,想明白,就拿本次来说,一定要把电梯的运行过程弄清楚,不然背后做的都是无用功,浪费时间,还会影响心态。
2. 刚开始写电梯时,想到的是C语言那一套来处理用户的请求(字符串)。其实在java中,我们可以使用正则表达式这个利器来处理字符串,效率非常高。当然在使用正则表达式时,要注意它的使用规则,在使用过程中也非常容易出错。
以下是常用且容易出错的地方。如果忘记如何使用的话,我们可以借助API帮助文档进行查询。
3.第一次写电梯用了太多的if-else结构,导致程序运行过慢,并且算法逻辑混乱,算法复杂度过高。例如在check函数中使用了太多的if-else进行判断,导致复杂度过高,而且三次算法复杂度最大的都在检查下一层楼层之中
4.有的变量没有遵守命名规则,在写代码的时候有时候还需要往前看,这个变量名到底表示什么意思。
5.我们要增强代码的复用性。每次写电梯基本上都是重头开始写,浪费了很多时间。如果代码的复用性得到实现,我们每次只需要在原来的代码上做修改和增添新的内容即可。
1.有的类还是没有没有遵守单一职责原则,一个类承担了太多的职责,在接上来应该细分
2.规范变量名、方法名等的命名,遵守驼峰命名规则。
3.电梯程序能处理大部分情况,但是从总体上来说这个电梯程序还是不对的,虽然通过了测试样例,还要改进。
4.完善注释,既要自己看得懂,也要让他人看懂。
1.这几次题目集让我认识到了正则表达式的强大功能,利器。同时也学会了正则表达式的使用。
2.在后两次处理用户请求时用到了LinkedList,比第一次的数组好用多了。LinkedList有很多方法可供我们使用。
同时在使用LinkedList的时候要注意,如果是基本数据类型,要将基本数据类型进行包装,例如int要变成Integer,double变成Double等等。
4.在接下来,要减少算法复杂度,从这三次电梯题目来看,算法复杂度每次都会增加。
5.写这次电梯的过程非常痛苦,但这确确实实能够增加我的能力,只能说又痛苦又快乐。加油吧,少年!