OO第二次博客做业

OO Unit2 总结

​ 第二单元的做业是面向多线程的电梯编程,经过这一单元的做业,笔者基本掌握了多线程编程的方法。算法

第五次做业

  • task:单部先来先服务调度电梯,用户请求能够在任意时刻到达。编程

  • 类图

    时序图

    设计策略安全

    虽然第一次做业仅仅只有一部电梯,可是须要一个专门的线程用于处理输入,与电梯线程并发执行。因为须要准备向多电梯扩展,我在此次做业设计时并无直接将输入给到电梯,而是通过了scheduler中转后,再由电梯得到而后执行。多线程

    此次做业我总共设计了两个线程,InputHandler和Elevator,共享对象是Scheduler。InputHandler将处理好的请求放入Scheduler的队列,ELevator将请求从Scheduler中取出而后执行。架构

    所以为了保证线程安全,须要对Scheduler的requests队列的操做作同步控制,具体体如今InputHandler向requests队列放入请求和Elevator从requests队列取请求时都须要先得到requests队列的锁。并发

    至于两个线程之间的协同体如今1.requests队列为空时,电梯须要等待,我采用的是CPU轮询的方法。2.InputHandler线程结束后,Elevator线程须要知晓,w为此我在InputHandler中设置了一个hasInputFlag的变量,标志是否还有输出,电梯能够经过查询该标志变量来检查输入线程是否结束,以便决定自身线程是否结束。框架

    优势函数

    • 输入处理和电梯模拟能够并发执行,程序运行效率高。
    • 电梯的运行逻辑分离程度较高,每个小步骤都写成了一个方法,而后用一些顶层方法包起来,主控制流只须要顺序调用这些顶层方法就能够了。

    缺点单元测试

    • CPU轮询过于消耗CPU时间。
    • Scheduler并无单独做为一个线程,可扩展性不够。
    • 电梯属性硬编码,可扩展性不够。
  • OO度量分析 -- 类

    • LOC:类代码行数。
    • OCavg:平均方法复杂度。
    • WMC:带权方法复杂度。
    • NAAC:类属性个数。
    • NOAC:类方法个数。
  • OO度量分析 -- 方法

    • CONTROL:控制语句数。
    • LOC:方法代码行数。
    • ev(G):核心圈复杂度。
    • iv(G):方法设计复杂度。
    • v(G):圈复杂度。
  • 设计原则检查

    • Single Responsibility Principle:每一个方法基本上都只负责一项功能的执行,符合单一职责原则。
    • Open Close Principle:因为存在硬编码等问题,可扩展性不高,对后续需求增长不利。
    • Liscov Substitution Principle:不存在子类的设计,知足里氏替换原则。
    • Interface Segregation Principle:不存在接口设计,知足接口分离原则。
    • Dependency Inversion Principle:没有考虑将楼层也抽象为对象,只是简单进行了楼层数的记录。

第六次做业

  • task:单部可捎带电梯实现,用户请求可随时到达。测试

  • 类图

    时序图

    设计策略

    本次设计较第一次有了较大的改变。因为调度器如今须要使用可捎带算法来为电梯分配请求,考虑到程序运行效率问题,调度器也能够做为一个单独线程与InputHandler和Elevator线程并发执行。

    此次做业笔者总共设计了三个线程:InputHandler、Elevator和Scheduler。InputHandler与Scheduler的共享区包括requests队列和inputOver标志;Scheduler与Elevator的共享区包括requests队列和runOver标志。

    为了保证线程安全,InputHandler对requests队列的放入操做与Scheduler对requests队列的取出操做都须要先申请锁;Scheduler将请求放入电梯的requests队列和电梯从本身的requests队列中取请求都须要先申请锁。

    本次做业线程间的协做体如今:首先由InputHandler接收输入进行处理后放入Scheduler请求队列;而后Scheduler执行调度算法,将请求分配给电梯;最后电梯执行运行算法,完成请求。为了实现三个线程间的同步控制,每得到一个输入后,InputHandler对Scheduler进行notify;当requests队列为空时,Scheduler处于wait状态;Scheduler每分配一个请求后,对Elevator进行notify;Elevator的requests队列为空时,Elevator处于wait状态。这样既能够保证不浪费CPU时间,又可以保证各个线程及时被唤醒工做。

    优势

    • 对楼层进行了建模,提升了可扩展性。
    • 将电梯的运行参数包装为runPara、runState两个属性类,即防止了硬编码提升了可扩展性,又不会让电梯的逻辑显得十分冗余。
    • 不一样类分工明确,相互之间只经过共享对象进行交互,耦合度很小。
    • 电梯设置了本身的请求队列,便于向多电梯的状况扩展。
    • 使用注册的方式,使得调度器能够知道电梯的信息。

    缺点

    • 电梯类的方法数过多,逻辑过于庞大。
  • OO度量分析 -- 类

    • LOC:类代码行数。
    • OCavg:平均方法复杂度。
    • WMC:带权方法复杂度。能够看到电梯类的方法的权值很大,代表其方法的调用次数和频率都很高。
    • NAAC:类属性个数。
    • NOAC:类方法个数。
  • OO度量分析 -- 方法


    • CONTROL:控制语句数。
    • LOC:方法代码行数。
    • ev(G):核心圈复杂度。
    • iv(G):方法设计复杂度。
    • v(G):圈复杂度。
  • 设计原则检查

    • Single Responsibility Principle:每一个方法基本上都只负责一项功能的执行;InputHandler和Scheduler类的职责也较为简单,可是Elevator类的功能过于复杂。
    • Open Close Principle:电梯运行参数、楼层都实现了建模,可方便的扩展和修改;调度器分配框架搭建完善,若是有调度算法上的改变,只须要重写调度函数便可。总的来讲,符合开闭原则。
    • Liscov Substitution Principle:不存在子类的设计,知足里氏替换原则。
    • Interface Segregation Principle:不存在接口设计,知足接口分离原则。
    • Dependency Inversion Principle:电梯内部其实能够进行进一步的抽象,好比将上行和下行的逻辑单独分离为一个运行器的类,这样也能够下降电梯类的复杂度。

第七次做业

  • task:三部运行参数不一样的电梯,使用可捎带调度算法,用户请求实时到达。

  • 类图

    时序图

    设计策略

    本次做为是多部电梯的运行,并且每部电梯的属性有所不一样。三部电梯之间是并发执行的。

    此次做业笔者总共设计了5个线程:InputHandler、Elevator和Scheduler。InputHandler与Scheduler的共享区包括requests队列和inputOver标志;Scheduler与Elevator的共享区包括每部电梯的requests队列和runOver标志。

    为了保证线程安全,InputHandler对requests队列的放入操做与Scheduler对requests队列的取出操做都须要先申请锁;Scheduler将请求放入电梯的requests队列和电梯从本身的requests队列中取请求都须要先申请锁。

    本次做业线程间的协做体如今:首先由InputHandler接收输入进行处理后放入Scheduler请求队列;而后Scheduler执行调度算法,将请求分配给电梯;最后电梯执行运行算法,完成请求。为了实现5个线程间的同步控制,每得到一个输入后,InputHandler对Scheduler进行notify;当requests队列为空时,Scheduler处于wait状态;Scheduler分配完当前队列中全部请求后,对全部的Elevator进行notify;Elevator的requests队列为空时,Elevator处于wait状态。

    此次做业还新增了换乘的功能,为了实现换乘,笔者的策略是在Scheduler中就首先拆分好,第一段交由电梯执行,第二段暂存在Scheduler的transfer队列中,当第一段被电梯执行完毕后,电梯通知调度器对第二段进行分配,这样就保证了第一段的永远先于第二段执行,实现了同步控制。而且因为transfer队列的存在,Scheduler线程结束的条件不只是输入结束和requests队列为空,还要算上transfer对列也为空才能结束线程,当全部请求分配完毕后,若transfer队列不空,Scheduler会进入wait状态,等待电梯通知。

    优势

    • 架构清楚,各模块功能界限清晰。
    • 进一步封装了Floor类,设计了Floors类,而且封装了Floor类的全部方法。
    • runState类提供了如up、down等方法,方便对电梯运行状态进行设置。
    • 电梯与调度器的交互经过addToFloor和awakeTransfer这两个方法完成,防止外部类直接对本类的属性进行改变。
    • 电梯调度改用了扫描算法,运行效率提升。

    缺点

    • 电梯类的逻辑过于庞大。
  • OO度量分析 -- 类

    • LOC:类代码行数。
    • OCavg:平均方法复杂度。调度器类中有分配算法的方法逻辑过大,所以致使平均方法复杂度提升。
    • WMC:带权方法复杂度。电梯和调度器的方法的权重都很高。
    • NAAC:类属性个数。电梯相对于第二次做业新增了一些属性。
    • NOAC:类方法个数。
  • OO度量分析 -- 方法



    • CONTROL:控制语句数。每一个方法的控制语句数都较少。
    • LOC:方法代码行数。
    • ev(G):核心圈复杂度。
    • iv(G):方法设计复杂度。
    • v(G):圈复杂度。调度器用于进行拆分请求的方法的复杂度略高。
  • 设计原则检查

    • Single Responsibility Principle:Elevator类的职责功能过多。
    • Open Close Principle:在第二次做业的基础上,新增了一些方法来知足新的需求;对电梯运行方法、调度器分配方法进行了修改,可是大致上是符合开闭原则的。
    • Liscov Substitution Principle:不存在子类的设计,知足里氏替换原则。
    • Interface Segregation Principle:不存在接口设计,知足接口分离原则。
    • Dependency Inversion Principle:电梯的抽象层次仍然不够,upFloor和downFloor的逻辑能够独立为类。

BUG分析

本身的BUG

​ 前三次做业在公测中都没有出现bug,因为未参加互测,所以没有进一步发现bug。

测试的方法

  • 静态检查:在重要的逻辑部分对代码作静态检查,好比调度器分配逻辑、电梯运行咯就。
  • 单元测试:在代码编写过程当中没写完一小部分独立性较高的方法就对其进行单元测试,保证无误后才进行以后的编写。
  • 综合测试:针对问题的规则,人为制造必定的复杂样例进行测试。对于换乘这种极容易出错的地方,进行重点的测试。
  • 回归测试:三次做业,每一次做业的测试用例都向后兼容,记录下前两次的用例用于第三次做业,节省了部分时间。

心得体会

多线程编程最重要的一点就在于线程安全的处理,凡是涉及到了线程共享的部分都须要进行加锁操做。这里就须要咱们找到到底哪些地方出现了共享,一般共享出如今参数传递、返回值、引用变量赋值这些地方,对于多线程编程中的这些场景咱们必定要格外注意,对于出现了共享对象,要追踪其全部使用位置,检查操做是否安全,不然一旦出现不可控的安全问题,靠debug来查找问题来源是极为困难的。

除了多线程编程外,这一单元笔者同时还接触到了设计原则。SOLID五大经典原则能够说是在实际开发过程当中应该随时恪守的,从需求分析到架构设计到代码实现,每一步都要注意设计原则,并且在实现完成后还要进行设计原则的检查,在不当的地方进行重构以符合设计原则。不少同窗可能很讨厌这种工做,认为又麻烦有没有意义,可是笔者认为在实际的软件开发过程当中,遵照设计原则是极其重要的。如今因为做业规模小,并且迭代开发次数少,不遵循设计原则看起来影响不大,一旦到了社会上,软件的开发每每规模庞大并且要通过不少次的迭代开发。在这种状况下,若是没有一个统一的设计规范,一我的一套规则,那相互之间的合做就会变得无比困难,别人开发的软件你可能彻底无法读懂,更别谈进行迭代开发;即便你可以读懂,若是该产品通过屡次迭代后,有些类的安全性、独立性等都已经发生了改变,那么要想在已有基础上进行修改,就可能须要向后追溯到好久之前实现的代码上去进行阅读,白白浪费大量时间。

总的来讲,设计原则应该贯穿于整个软件开发的过程,遵照设计原则,能够帮助咱们更好的进行设计和开发。

相关文章
相关标签/搜索