zoukankan      html  css  js  c++  java
  • OO第二单元总结

    单元总结

    第一次作业

    一、设计策略

    这次的电梯只有一个,但要求支持捎带。对此,我采用的是look算法,电梯每到达一层访问一次请求队列,因为电梯无人数限制,若电梯在该层有需要进出的请求,则执行,同时在电梯关门前再次确认是否还有需要进出的请求,用以缩短乘客等待时间,提升电梯性能,电梯运行直到没有当前方向上的请求(电梯内无目的地更高的请求,电梯外无在更高层等待进入电梯的请求),则换向。

    多线程设计

    1、设计模式:采用 Producer-Consumer 模式,作了进一步的拓展,Inputt线程为Producer,Elevator线程为Consumer。

    2、共享对象:由 Inputt Elevator共享一个请求队列。

    3、线程安全:为了遍历和存取方便,存放请求的数据结构为了较为熟悉的 ArrayList 。为了保证线程安全,将队列的add()与remove()用synchronized上锁以确保线程安全。

    二、SOLID原则分析

    Single Responsibility Principle:Inputt线程负责读入请求,“实时”更新可捎带队列,Elevator 则根据自身内部的乘客队列以及各楼层的可捎带队列工作并接送人,执行LOOK算法。整体来看比较符合类功能的独立性。

    Open Close Principle:本次架构对于之后的多电梯具有可扩展性(但由于电梯内部run方法逻辑复杂,以及新增的对电梯可停靠楼层以及载客量、速度等的限制,最终还是选择了重写该类)

    Lisov Substitution Principle:未使用继承

    Interface Segregation Principle:未使用接口

    Dependency Inversion Principle:未使用抽象类

    三、度量分析

    1、

    本次代码中有些该封装的地方没封装,该复用的地方没复用,所以导致某些方法比较冗长。比如电梯线程的close(),if1(),If2(),run()方法中,类间耦合度较高,将细节都暴露了出来,复杂度较高,有不少重复的语句,应该把代码风格优化一下。

    2、UML

    从类图中可以看到,一共有五个类,其中:

    1)数据类:

    State

    该类用于保存电梯的各种状态,改变并得到电梯的各种状态,避免硬编码,降低耦合度。

    Task

    为请求类,存放请求信息(当请求进入电梯后,in属性置位)。

    (2)线程类

    Elevator

    属性包括:电梯的状态,电梯与输入的共享队列。

    Inputt

    属性包括:电梯与输入的共享队列。

    (3)入口类

    Mainclass

    创建了一个共享请求队列,电梯线程和输入线程,将这个共享请求队列作为电梯线程和输入线程的

    优点:

    实现了请求队列与调度器分离,可扩展性好。

    不足:

    电梯内部逻辑混乱,写的不优美,将主要的分配安排给了电梯,导致电梯类复杂度较高。

    四、程序bug分析

    本次公测与互测均未被发现bug。

    由于是第一次多线程作业,我还有些生疏,在反复学习研读多线程相关知识后,我才真正掌握对共享变量的保护机制,并在编写代码的过程中十分注意共享变量的保护,以及会不会出现死锁等等,从结果来看我的此次作业有做到“线程安全”。

    五、互测分析

    在互测中我先是对“线程安全”进行检查,再查看所有线程能否结束,并采用JProfiler来试着hackCPU运行超时方面的bug,同时也采用了我自己测试时用的数据样例,但最后均未发现对方bug。与第一单元相比,本单元互测更注重多线程行为,应注意线程安全、死锁、线程轮询造成的CPU超时等。

    次作业

    一、设计策略

    相比前次,最主要的区别就是由单电梯变成了多电梯,那么如何处理好多个电梯之间的协作关系就成了主要问题。而事实上,所谓的协作关系也只是保证线程安全性,在这个基础上采用电梯间相互争抢的模式还是由输入线程分配呢,我反复权衡下采用后者,调度器分配请求时将请求分配给同方向的电梯,若是上行请求,则电梯当前楼层要小于请求起始楼层,若是下行请求,则电梯当前楼层要大于请求起始楼层,后来的性能分也证明效果不错。

    在电梯调度算法方面,我采用了上次作业的LOOK算法的优化版:增加了电梯的目的地属性,将换方向的判断条件改为当电梯已到达目的地时,由于此次电梯有人数限制,我同时增加了“内部请求队列”这一属性来管理电梯内的请求,为保证电梯人数小于载客量并尽可能多地载客以提高程序性能,在每层进出人时采用“先出后进”,

    多线程设计

    1、设计模式:仍旧采用 Producer-Consumer 模式,只是作了进一步的拓展。

    2、共享对象:由 Inputt和每部运行 Elevator分别共享一个请求队列。

    3、线程安全:为了遍历和存取方便,存放请求的数据结构为了较为熟悉的 ArrayList 。为了保证线程安全,将队列的add()与remove()用各自的synchronized()上锁以确保线程安全。

    二、SOLID原则分析

    Single Responsibility Principle:Inputt线程负责读入请求并对请求进行分配“实时”更新可捎带队列,Elevator 则根据自身内部的乘客队列以及各楼层的可捎带队列工作并接送人,执行LOOK算法。整体来看比较符合类功能的独立性。

    Open Close Principle:本次架构具有一定的可扩展性,但由于电梯功能设计有些复杂,导致代码逻辑不够清晰。

    Lisov Substitution Principle:未使用继承

    Interface Segregation Principle:未使用接口

    Dependency Inversion Principle:未使用抽象类

    三、度量分析

    由图中可以看出 Inputt类和Elevator类复杂度较高,由于 Inputt线程对分配的各种情况进行特判,Elevator线程对运载请求的各类情况的判断,导致run()方法、Elevator类的juedingmudidi()、if1()、if2()、close()方法复杂,耦合度较高,因此在类的结构设计上还需进一步优化。

    类设计

    1、数据类:

    Request

    为请求类,存放请求信息。

    2、线程类

    Elevator

    属性包括:电梯的状态(其中增加了最大载客量、运行速度、开关门时间),电梯与输入的共享队列。

    Run()方法执行LOOK算法。

       Inputt

    属性包括:电梯与输入的共享队列。

    Run()方法安排电梯运行并执行请求分配。

    3、入口类

        Mainclass

    初始化五部电梯。

    优点:
    1、五个队列的设计使得“线程安全”较易实现,可扩展性强。

    2、Inputt线程分配请求, Elevator线程执行请求,各类功能逻辑清晰。

    不足:

    Inputt类和Elevator类负责的任务过于复杂,类中方法耦合度较高。

    四、程序bug分析

    这次我采用了自动评测机。并且发现程序中的严重bug,就是某些情况下电梯wait()后就再也醒不过来了,原因在于notifyAll()的逻辑不太对。

    相比单线程,多线程debug的确比较难受,不过用print方法,在每个睡眠、唤醒等关键环节都print出相关信息,比较快地定位到了有问题的代码片段。最后也成功地过了互测和强测。

    互测分析

    我在互测中采用了自动评测机,未发现对方bug。

    第三次作业

    一、设计策略

    本次作业在研究完指导书后发现最令人头疼的便是换乘请求的处理,在架构上也是设计一版扔一版;而在性能上,考虑到多电梯调度的复杂性以及官方保证了真·随机数据,最后还是对上次作业的LOOK算法稍作改动:对于需要换乘的请求暂时不运行,确定电梯在一次上下行中时无需考虑这些请求。

    多线程设计

    1、设计模式:仍旧采用 Producer-Consumer 模式,只是作了进一步的拓展。

    2、共享对象:由 Inputt和每部运行 Elevator分别共享一个请求队列。

    3、线程安全:为了遍历和存取方便,存放请求的数据结构为了较为熟悉的 ArrayList 。为了保证线程安全,将队列的add()与remove()用各自的synchronized()上锁以确保线程安全。

    二、SOLID原则分析

    Single Responsibility Principle:Inputt线程负责读入请求并对请求进行分配“实时”更新可捎带队列,Diaoduqi类封装一些调度方法,Elevator 则根据自身内部的乘客队列以及各楼层的可捎带队列工作并接送人,执行LOOK算法。整体来看比较符合类功能的独立性。

    Open Close Principle:本次架构具有一定的可扩展性。但还应在Inputt及Elevator的功能复杂度设计上多做优化。

    Lisov Substitution Principle:未使用继承

    Interface Segregation Principle:未使用接口

    Dependency Inversion Principle:未使用抽象类

    三、度量分析

    由图中可以看出 Inputt类、Elevator类以及Mainlass主类复杂度较高,由于 Inputt线程对分配的各种情况进行特判,Elevator线程对运载请求的各类情况的判断,以及未能很好得对一些重复出现的情况进行归类,导致各类功能复杂,run()方法、Elevator类的juedingmudidi()、jinlaichuquduilie()、suozhu()、shuimian()、Input类的xixi()方法耦合度较高,因此在类的结构设计与功能分配上还需进一步优化。

    类设计

    1、数据类:

    1)MyRequest

        为请求类,存放请求信息,其中增加了换乘请求以及当下是否能被执行的属性,使请求换乘的逻辑更为清晰。

    2)Diaoduqi

        该类封装了一些分配请求的方法。

    2、线程类

    1)Elevator

    属性包括:电梯的状态(其中包括最大载客量、运行速度、开关门时间、可抵达楼层等),电梯与输入的共享队列。

    Run()方法执行LOOK算法。

    (2)Inputt

    属性包括:电梯与输入的共享队列。

    Run()方法动态加入电梯并执行请求分配。

    3、入口类

    1)Mainclass

    初始化ABC三部电梯。

    不足:

    本次作业加入了换乘以及动态带调度电梯,我对各个类的功能分配及封装没有很好地处理,导致 Inputt类、Elevator类、MainClass主类之间耦合度过高,负责的功能过于复杂,类中方法的耦合度也过高,代码风格需完善。

    四、程序bug分析

    本次作业我依旧采用自动测评机,并在中测阶段发现了CPU超时的bug,原因是在电梯的请求队列中只有换乘请求时电梯会一直轮询,我加入了sleep()解决了CPU超时的bug。在强测和互测中未被发现bug。

    五、互测分析

    我在互测中采用了自动评测机,未发现对方bug。

    心得体会

    三周的多线程之旅已然结束,这期间当然又踩了很多坑,也吃了不少经验教训。这三次作业层层递进的,难度也一次次加大,尤其是第三次的多电梯,对设计架构的要求很高,如果在最开始没有把各个类之间的关系梳理清楚,那么功能实现起来就会重重受阻,甚至面临全盘重构的可能。

    其中最重要的是“线程安全”,无脑的加锁不仅会破坏原有架构,更重要的是很可能有逻辑漏洞,导致死锁或者出现其他一些令人意想不到的结果。一个好的设计模式,一个好的架构,一定是兼具正确性与扩展性的,对于我来说,输入推入请求,电梯取出请求,这种"生产者-消费者"模型的核心没有变。只要能设计好架构,让耦合度降到最低,那么任意电梯数量、各种限制要求都可以轻松地满足。

    通过这个单元的训练,我对多线程编程有了深入的了解,掌握了线程间通信、同步、互斥的方法,保证线程的安全性。更重要的一方面是,设计架构的能力有了不小的提升,明白如何才能设计出高内聚、低耦合的程序,这对以后可能的企业工作将有巨大的帮助。

  • 相关阅读:
    MyBatis之启动分析(一)
    通俗算法教程03
    数学倒底有没有绝对的严格性和形式化?
    通俗算法教程02
    .NET Core 中正确使用 HttpClient 的姿势
    通俗算法教程01
    我要写一篇文章吗?
    [ASP.NET MVC 小牛之路]18
    [ASP.NET MVC 小牛之路]17
    [ASP.NET MVC 小牛之路]16
  • 原文地址:https://www.cnblogs.com/zzzzzzzz0923/p/12727775.html
Copyright © 2011-2022 走看看