OO第二单元作业总结

  我是一个经常会坐电梯的人,从教一的一楼坐到四楼,从实七的一楼坐到七楼,电梯里的人进进出出,电梯上上下下,我偶尔会盯着电梯显示的楼层不断变化,心里想着电梯到底是按照什么样的规则运行的,但是这次面向对象的作业却让我成了设计电梯的人,这确实是一次很贴近现实的编程体验。

 

三次作业的基本设计

第一次作业:第一次作业的设计非常简单,我的设计分为四个部分:一个Main,一个请求输入部分,一个调度器,还有一部电梯,这个设计在我的第二次作业的电梯也得到了应用,请求部分负责输入,当接收到输入就传送给调度器,调度器则利用一个队列将请求存放起来,由于第一次只需要使用简单的傻瓜调度,所以电梯每当执行完一条指令,就去向调度器索要一条新的指令去执行,然后将请求从请求队列中删除即可。结束程序的逻辑也很简单,当电梯向调度发出请求,但是输入为null并且请求队列已经结束时,就结束所有线程。

第二次作业:第二次作业的设计继承了第一次作业的大部分内容,仍然分为四个部分:Main,请求输入部分,一个调度器,还有一部电梯,调度器仍然利用一个队列将请求存放起来,但是第二次作业要求实现捎带,所以对请求的处理不再和第一次那么简单。我的处理方法是,在电梯里也设置一个队列,用来存放主请求和捎带请求,电梯每到达一层楼,不管是否需要开关门,都执行一个判断,具体内容是,在调度器内对请求队列进行遍历,如果某个请求的起始楼层和当前电梯到达的楼层相同,就让这样的请求进入电梯队列,输出相应的开关门和人员进入,然后当主请求运行时,到达每一层还需要进行另一个判断,就是此时电梯中的队列,是否有请求到达,如果有,就执行该请求的出电梯,当主请求结束后,结束电梯队列的请求运行,然后再从调度器获取一个新的主请求,就这样进行下去,当电梯获取不到请求并且程序未结束时,让电梯线程wait,当有输入进入调度器队列时用notify唤醒即可。结束程序的逻辑也和第一次相同。

第三次作业:第三次作业要求三部电梯运行,我的电梯调度直接借用了第二次的捎带算法,但是不一样的是,这一次我在调度器中使用了三个队列来存放请求,分别对应着三部电梯,但是第三次作业中有一个很关键的问题,那就是部分请求需要由多部电梯联合执行,首先为了确认是否有请求需要三部电梯联合执行,我编写了如下的代码来对所有可能的请求进行枚举,由于代码较长,这里用伪代码代替:

创建三个数组记录A、B、C电梯可以停留的楼层
双重循环枚举请求{
    如果某个电梯可以执行,继续枚举下一个请求;
    如果起点和终点分别可以被两个电梯执行,就从-3楼到20楼遍历,如果发现某一层两个电梯都能停靠
    ,继续枚举下一个请求;
    如果前两个判断都失败,那么说明有请求需要三个电梯共同执行。
}

从程序的运行结果来看并没有需要三部电梯共同协作的请求(由于输出过于冗长,这里不作展示)。接下来就是我个人觉得整个程序最关键的设计点了,当调度器中从输入类中得到一条请求,经过判断发现这条指令需要两部电梯来共同执行,那么这个时候应该怎么办呢?我的设计方法就是:首先将请求送入第一部电梯执行,然后在执行完毕之后,将这个请求返回调度器,进入拎一个电梯的请求队列。但是这里有一个小问题,我们怎么样标记一条指令是否已经被执行,怎么样更改换乘请求的起始楼层呢。这里我新建了一个类,在这个类中对PersonRequest进行了扩展。具体代码如下:

public class Personrequests {
    private PersonRequest personRequest;
    private int fromFloor;
    private int toFloor;
    private int personId;
    private int iftrans;
    private int newfromfloor;
    private int needTrans;
    public Personrequests(PersonRequest req) {
        personRequest = req;
        fromFloor = req.getFromFloor();
        toFloor = req.getToFloor();
        personId = req.getPersonId();
        iftrans = 0;
       newfromfloor = 0;
        needTrans = 0;
    }
    public int getFromFloor() {
        if (needTrans == 1 && iftrans == 1) { return newfromfloor; }
        return fromFloor;
    }
    public int getToFloor() {
       if (needTrans == 1 && iftrans == 0) { return newfromfloor; }
        return toFloor;
    }
    public int getPersonId() {return personId;}
    public int getIftrans() { return iftrans; }
    public int getNeedTrans() {return needTrans;}
    public void writeNeed() {needTrans = 1;}
    public void writeIftrans() {iftrans = 1;}
    public void writeNewfloor(int from) {newfromfloor = from;}
    public int getRealTo() { return toFloor; }
}

这个类在第一步电梯执行完之后会进行标记,然后返回的起点就是进过计算的中转楼层。这样就成功的解决了怎么样进行中转的问题。但是在我提交中测的过程中,一直会遇到程序提前结束的问题。最后经过调试,我发现调度器提前停止了运行,如果沿用前两次的暂停逻辑,那么当输入为null并且调度器请求队列为空时就结束,那么此时仍然有可能有指令正在进行第一次中转,还没有来得及返回调度器。所以我在调度器中添加了一个count变量,用来判断中转请求是否执行完毕,只有当同时满足输入为null、中转指令全部运行完毕、调度器请求队列为空时,调度器才结束运行。

到这里,三次电梯的设计部分就全部结束了。

 

类图和度量分析

第一次作业

 

 

第二次作业

 

 

第三次作业:

 

 

代码复杂度:

 

 

其实三次作业的架构思路基本一致,但是在这里我必须反思自己在第三次作业中仍然留存的一个问题,就是将三个电梯分成了三个电梯类来写,会显得整个程序有很多冗余重复的代码,对电梯进行修改时也会遇到一个问题要在三个类中同时进行修改的麻烦,这应该算是我对第三次作业最不满意的地方所在。

 

测评中出现的bug

第一次作业并没有出现bug,因为整体结构十分简单,对性能的要求也并不高,所以不太可能会出现致命的bug,甚至不太可能出现bug。

第二次作业就出现了很多问题,甚至导致了强测的翻车,首先我在电梯的调度方面出现了很多问题,在人很多的情况下会发生混乱,出现了有些楼层带不上人的现象,其次是我的电梯部分在多人进入时有概率出现不断开关门导致消耗时间变长的现象。

第三次作业最后只出现了一个bug,就是当某个电梯正处于停留状态,然后请求需要中转从这个电梯进入时,会发生们没有开但是人想要进入的情况,在我添加了一个新的电梯状态之后,问题就得到了解决。

 

关于互测

第二单元的互测相比于第一单元复杂了不少,既要考虑输入的时间又要考虑如何判断一个程序的输出是否正确,这着实在互测上给我带来了不小的困扰,但是这次作业其实很容易在逻辑上找到一些漏洞,尤其是第三次作业,非常容易在程序的结束部分和换乘部分出现意想不到的bug,这也直接导致我随机盲刀了几次也能中刀(但是非常不推荐这种做法),好好学习如何去写测评机,才是我下一步要去努力的目标。

 

一些编程中的心得

其实第二单元和第一单元相比代码量小了许多,主要是在考察多线程有关于线程安全保护的知识,哪些时候哪些线程可以并行,哪些线程需要被锁住,这具有很强的逻辑关联,对我们关于程序各个阶段的执行顺序的理解提出了很高的要求,我们要做的就是在确保线程安全的前提之下,尽可能让更多的线程并行,以提高效率,要实现这个目标并不容易,首先要建立好程序的框架,相应的类对应相应的职责,相互限制。从这个单元的作业上,我也是成功地在后两次的作业中应用了第一次的电梯,和第一单元相比也是有了一定程度的进步。希望下一个单元自己可以做的更好。

posted on 2019-04-22 00:05  S1mpleee  阅读(194)  评论(0)    收藏  举报

导航