zoukankan      html  css  js  c++  java
  • 面向对象第二单元总结

    前言

      第二单元的三次作业围绕电梯展开,近一段时间里我们学习了多线程的相关知识,也体验了传说中的魔鬼电梯,我对多线程的理解也从一开始的仅仅了解名词到能够运用知识创造“电梯s”,虽然自以为在这一个月中收获不小,但自己也很清楚学习到的仅仅是皮毛。总的来说,这个单元的前两次作业比较简单,第三次作业则比较玄学,自己也因为感觉前两次架构不够可靠所以在第三次作业改动较大,借此次博客从以下几个方面对第二单元的作业进行总结与反思。

     一、作业回顾

      第一次作业

      第一次作业是单部多线程傻瓜调度(FAFS)电梯的模拟,想法比较简单,没有什么算法可言,一个输入线程(Input),一个电梯运行线程(Elevator),现在想来第一次作业的不足是没有构造单独的调度器,都是电梯自己在运行。

      度量分析

      

      

      第一次作业总的来说比较简单,通过ElevatorIn输入请求并存在请求队列RequestQueue中,采用FIFO原则,电梯每次只完成一条指令,当输入为空且请求队列为空时,程序结束。

      公测及互测bug分析

      第一次作业比较友好,主要是为之后的作业铺垫,在公测及互测中未发现bug。

      自己在写第一次作业的时候出现了程序无法正常结束的问题,在ctrl d之后依然可以输入,只是程序不再对输入进行处理,后来发现是多个线程之间的通信信息没有设置好,这一点的发现对后两次作业的进行具有重要作用。

      第二次作业

      第二次作业是单部多线程可捎带调度(ALS)电梯的模拟,第二次的电梯比第一次高级了一点,整体思路是一样的所以没有重构,只是在第一次作业的基础上进行修改,每个人具有status标志,用于判断在电梯内还是电梯外或是已完成请求,电梯每到一楼层就遍历队列中各人员请求,以此来决定电梯的行为。我想最大的缺点应该是电梯承担了太多任务,没有把功能隔离开来各司其职。

      度量分析

      

      

      

      第二次作业延续了第一次作业的设计,只是在Elevator类里增加了遍历请求队列以确定电梯行为的功能。

      公测及互测bug分析

      第二次作业在强测中有一个点的运行时间超过了Tmax,总的来说调度方法比较死板,比如此时电梯在3楼要去10楼,而此时有一个请求2号是从5楼到2楼,那么电梯会把2号运到10楼再送回2楼,这不符合实际情况。还有不足的是自己在优化上没有好好思考,未采取效率较高的调度方式。

      第三次作业

      第三次作业是多部多线程智能(SS)调度电梯的模拟,这次作业的调度方式给了我们很大的发挥空间,依然采取了ALS调度策略,跟前两次不同的是限制条件更多,需要考虑换乘问题,所以增加了调度器类和换乘类,用于协调请求和电梯之间的关系。写之前感觉实现方法有很多种,思路比较混乱,后面慢慢理清写完,发现debug也很磨人,但是这次作业是让我对多线程理解最深入的一次作业,对面向对象思维也有了更多的认识。

      度量分析

      

      

      

      

      第三次作业相较前两次改动较大,有一个主队列接收输入,各个电梯分别有一个请求队列,由Controller负责分配调度,同时增加了处理换乘的Connect类,若是不能直达则将请求分为两个,人员到1楼或15楼进行换乘。

      公测及互测bug分析

      这次的公测和互测情况不那么理想,由于换乘的调度不当以及修改了电梯内部的运行方式,导致多个换乘指令便会出现StackOverflowError报错,一个原因是在设计架构的时候不够仔细,还有一个原因是自己在进行测试的时候太过敷衍,没有认认真真构造测试样例进行检查,也当做是收获了一个教训吧。

    二、基于SOLID原则的评价

    SRP The Single Responsibility Principle  单一责任原则
    OCP The Open Closed Principle  开放封闭原则
    LSP The Liskov Substitution Principle 里氏替换原则
    DIP The Dependency Inversion Principle 依赖倒置原则
    ISP The Interface Segregation Principle 接口分离原则

    1.SRP

      类Input,Elevator,RequestQueue,Connect,Controller等较为合理的实现了功能单一性,但依然出现了个别类负荷过重的情况。根据单一职责原则,当一个类具有了多项职责,它需要被更改的可能性也随之增加。每次一个类的修改也会使得bug产生的风险增加。而通过集中职责与一点会使得风险被有效的限制。

    2.OCP

      开闭原则指出:“软件实体(classes, modules, functions etc.)应该对拓展开放,对修改关闭”。与SRP一样,这项原理通过限制对现有代码的更改来降低引入新错误的风险。跟第一单元三次作业相比,自己感觉在程序拓展性的处理上有了进步(或许是跟程序本身有关),虽然第三次作业跟前两次相比改动较大,但主要是增加模块处理新的需求,完成调度工作,比较符合开闭原则。

    3.LSP,ISP,DIP

      在这次作业中没有使用到借口和继承(排除Runnable),应该可以实现三个子类电梯,继承父类电梯,但是感觉这样的写法比较麻烦,也没有必要。

      虽然在本单元作业设计中未使用到接口和继承,但是也算对SOLID原则有了初步的了解,对以后的作业也有指导作用,希望在之后的作业中我能渐渐向这五项原则靠拢。

      里氏替换原则(LSP)声明:“所有引用基类的地方必须能透明地使用其子类的对象”。如果你创建了一个给定类型关系的类,那么你应该可以提供该类型或任意该类型子类的对象,而不会出现意外的结果,并且没有依赖的类知道被提供依赖类的确切类型。如果必须检查依赖关系的类型,以便可以根据类型修改行为,或者如果子类型产生意外的规则或副作用,则代码可能变得更加复杂,僵化和脆弱。

            依赖倒置原则(DIP)有两条声明。第一个是高级模块不应该依赖于低级模块。两者都应该依赖于抽象。第二部分规则是抽象不应该依赖于细节。细节应该依赖于抽象。DIP主要涉及到应用中层次化的概念,其中较低级别的模块处理细节的功能,较高级别的模块使用较低级别的类来实现更大的任务。该原则规定了在类之间存在依赖关系的情况下,应使用抽象(如接口)来定义它们,而不是直接引用类。 这减少了由较低级别模块的变化导致的错误,导致较高层的错误。 DIP经常在依赖注入中被使用。

      接口分离原则(ISP)指出:“客户端不应该强制依赖那些他们没有使用到的接口”。这个规则意味着当一个类依赖另一个类时,接口中可以被依赖类显示的成员的数量应该被最小化。通常当您创建一个具有大量方法和属性的类时,该类将被其他类使用,并且只访问其一个或两个成员。随着他们意识到的成员数量的增加,这些类更加紧密地耦合在一起。当您遵循ISP时,大类实现了多个更小的接口,根据用途对功能进行分组。依赖关系与那些相关联用于松耦合,增加健壮性,灵活性以及可复用性。
     
    摘自:https://www.jianshu.com/p/1c6498da3862

    三、关于互测

      跟上一单元相比,感觉这个单元的互测难度是增大了的,不仅与楼层有关,还涉及到时间。在互测中,一开始是会用同学的评测机(感谢各位大佬!)对屋里的代码进行测试,偶尔会发现大家设计疏漏的地方(也会发现自己的bug)。时间充足的话会去分析其他同学代码的设计结构,会发现大家的实现方法都不太一样。比如在第三次作业中就看到有同学是针对三个电梯分别写了三个类,还有大家采取的不一样的调度策略,有的同学是FAFS调度,而有的同学的设计则是比较符合实际生活中的电梯运行模式。总的来说,我感觉互测能够给我们很多启发。

    四、心得体会

      1.线程安全

      在多个线程访问共享数据时,会导致线程安全问题产生。

      线程安全在三个方面体现:

      1.原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);

      2.可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile)

      3.有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)。

      总结起来,确保线程安全的设计有三个关键要素:

      1.控制对象发布和共享;

      2.共享对象设计为线程安全类;

      3.保持简洁的线程类。

      2.设计原则

      在动手写代码之前,需要有整体的构思,要注意识别并发行为,简化类方法的职责,设计类之间的协同以及空间与时间的平衡。

      在实现代码之后的设计检查中,除了要重视SOLID原则,我觉得在本单元中还需要关注以下几点:

      1. 局部化原则,类之间不要冗余存储相同的数据,方法之间不能够出现控制耦合;

      2.完整性原则,一个类需要提供针对相应数据进行处理的完整方法集;

      3.显式表达原则,显式表达所有想要表达的数据或逻辑,不使用数组存储位置或者常量来隐含表示某个特定状态或数据;

      不管在哪个阶段,都要提醒自己,正确性最重要!

      3.反思

      转眼间这学期已经过半啦,经过这三次作业,我对多线程有了更深入的了解,最近OS也在学习多线程有关的知识,两门课程都能互相补充到一些东西。自己在写代码的过程中,由于理论知识储备不够,常常会有力不从心的感觉,我想我还需要时间去积累理论知识并且学着应用。

      道阻且长。

  • 相关阅读:
    axios 进行类库封装
    vue的中vuex为何需要mutation更新状态,vue-router的路由的理解
    发布订阅者模式、观察者模式总结
    es 模块的基础知识,深度了解
    绑定事件的模型
    rem+media+jquery布局结局方案
    VDOM总结
    react-redux
    发布网站配置文件和SSL
    css3d旋转
  • 原文地址:https://www.cnblogs.com/venturenn/p/10758618.html
Copyright © 2011-2022 走看看