对象不止是一个对象——面向对象设计与构造第四章总结暨课程总结

对象就像山,横看成岭侧成峰,远近高低各不同;

对象就像国家,家是最小国,国是千万家;

一个对象,其实不止是那一个对象……

一、UML单元作业架构设计

本单元的作业根据给定的UML元素实现多种类型的查询和模型正确性的预检查,在无运行时间限制的情况下,理论上每次查询皆可结合原始UML数据进行。但现实情况是对复杂度有限制的,因此需要以空间换时间的进行优化,具体思路分为处理数据时,增加所需要UML元素的命中率(可至100%)多次相同查询处理时间的压缩。从而引出了作业中索引和缓存的思想。

UML单元作业共有两次,第二次作业沿用了第一次作业的框架,并对第一次作业的任务完全兼容,在此仅列出第二次作业的文件树结构,并对各部分做相应的解释。

├─compoent
│  │  
│  ├─interaction
│  │      InteractionNode.java
│  │      LifeLineNode.java
│  │      
│  ├─model
│  │      ClassInterfaceModel.java
│  │      ClassNode.java
│  │      InterfaceNode.java
│  │      OperationNode.java
│  │      
│  └─state
│          RegionNode.java
│          StateMachineNode.java
│          StateNode.java
│          
├─handler
│      AddElementHandler.java
│      GeneralHandler.java
│      
├─main
│      Main.java
│      MyUmlGeneralInteraction.java
│      
├─navigate
│      IdToUmlElement.java
│      NodeNavigator.java
│      
├─precheck
│      Tool.java
│      Uml002Exception.java
│      Uml008Exception.java
│      Uml009Exception.java
│      UmlGraphException.java
│      
└─tool
        JsonFactory.java
        VersionCache.java

  1. compoent
    1. 该包中的三个子包分别通过对类图、状态图和顺序图的支持。对于每类UML图,定义了一些Node类作为原UML元素的外壳(选择哪些UML元素拥有外壳是由查询指令的特征所决定的)。
    2. 每个Node类外壳除了能够提取出原UML外,拥有额外的空间记录下级元素和与同级元素的关系。以类图中的ClassNode为例,除了内部的原UML元素外,还存有:
      1. 拥有的操作的列表。List<OperationNode>
      2. 拥有的属性的列表。List<UMLAttribute>
      3. 实现的接口列表。List<InterfaceNode>
      4. 继承的类列表。List<ClassNode>
    3. Compoent中的类外壳实现了对原始UMLElement的有序逻辑性地组织,很大地提高了索引效率,以便在特定查询时高效(100%)地索引和使用,提升效率。
  2. handler:类比于操作系统中的异常向量设计,Handler对各种UMLElement子类识别和执行对应的处理。
  3. navigate:由于在处理阶段和查询阶段均涉及大量以nameid为键值的查找,因而在此包中使用单例模式定义了两个负责此类任务的类,是索引思想的另一处体现,包中的两个类分别维护特定键值到原始UMLElement和外壳类的索引功能,对于原始UMLElement,键值维护仅有id,对于外壳类,键值维护id和name。
  4. precheck:precheck中含有用于UML类图错误检查的类,在检查时,类以单例的navigator包中类为入口,对建立好的类图进行访问和检查。其中002UmlRuleException类错误朴素检查,008UmlRuleException009UmlRuleException涉及图论内容,建图和访问图的部分方法可共用,因此两类错误的检查均继承自抽象类UmlGraphException
  5. tool:辅助工具包
    1. JsonFactory:生成UML元素的Json型参数,用于单元测试与代码建模。根据官方包支持,可以使用LoadFromJson创建所需要的UML元素,因而可以代码建模并开展可全自动的单元测试。
    2. VersionCache:修改和查询的版本控制和缓存支持。此处运用了缓存思想,并对版本控制与缓存维护功能进行了统一的封装。一个实例对应一个有缓存需求的查询修改实体,通过自定义的id,可实现对多种查询结果的缓存,实例中有modifygetupdate三种方法,modify指令对应修改,getupdate指令对应查询。

二、四次作业架构设计与对OO方法理解的演进

架构设计直接体现了对于对象概念的理解,在四章OO作业的架构设计中,我认为在架构时我对于对象的辨析主要分为三种类型:层次对象,功能对象和线程对象,这三种对象概念并不完全互斥,在四次作业中都多多少少使用过,在此我仅选择几种类型最为代表性的作业作为例子进行解析。

层次对象:第一章函数求导与第四章UML解析

我认为,层次化对象体现了最为直接的对象,在架构时,我常常采用“勾画描述中的对象并探讨之间的关系”。

层次化对象的架构有两个方面,一是以继承为基础的抽象通性与共性行为的构建,二是对象在等级上的树状管理。在第一章作业中,各类因子类和操作类具有大量共性,因而采用继承方法,而对于表达式的解析则直接采用树状结构适应;在第四章作业中,UML元素之间具有显著而确定的层次关系,因而在构建和维护时使用层次化管理的方式。

功能对象:第三章JML-地铁线路查询

功能对象的特点是以功能实现为目的,将一群拥有同样功能的对象(也许这些对象以层次对象形式组织)视作一个对象,在对象之间开放协作的接口实现整体功能的实现。

在这种结构下,功能如何划分?对架构是一个关键问题,功能的拆分一般不能直接从描述中的关键词得到,而需要更深的挖掘与更多的经验,在四次作业中,一个通用的功能对象设计就是IO与具体功能实体的分离,而在JML单元的作业中,这种思想更体现在地铁线路中图的物理结构与跑在图上的算法的分离:图的物理结构以点和边两种形式进行维护,只提供图中的关系而不提供任何结论;而染色、最短路等图论算法则根据图结构所提供的接口获取需要的数据进行处理。

线程对象:第二章线程-电梯调度

在我看来,线程对象架构是一种异步型的功能对象架构,衔接不同功能的对象的桥梁从同步调用变为异步信息,因此在功能对象的概念上,重点要解决的内容是线程的异步通信

第二章的电梯调度作业其实已基本固定了架构,采用Clientdispatcherelevator三对象的结构实现,难点是异步消息发送的实现,此功能若实现不好,会导致线程功能异常与理想的架构之间出现严重矛盾与顾此失彼的现象。我在OO研讨课中有重点分享了有关线程通讯的观点与经验,在此简要总结:

  1. 基础结构——托盘类:实现对线程内部数据的无忧无虑保护,将线程间的安全问题缩小至一个托盘类。
  2. 性能优化——(Guard Mode):使用notify和wait方法杜绝线程轮询与忙等待,但Guard模式的引入将使得一个线程的信息入口完全局部化至一个唯一的托盘。
  3. 利用接口实现邮箱,解决基于托盘+Guard的多类信息接收:对于同一个线程,向其发送的信息均需要实现统一的接口,便于线程接收和解析来自多个线程的多类消息,利用Handler进行处理。
  4. 异常情况的外部接入——调度交互:托盘类的实现使得线程内部完全封闭,出现问题外部无法通过数据交互进行干涉,因此需要引入以interrupt, interruptted等为基础的线程交互,该部分并没有进行训练和实际应用,有待挖掘。

img

小结

总的来说,在我进行架构设计的过程中,对象的识别和建立是非常重要的,从接触OO最初时能够从描述中直接感受到的关键词对象,到后来从功能维度和线程维度等建立更具有抽象性的对象,在不同的阶段和层次有着不同类型的对象。我在四次OO作业中从低到高地体会到不同层次的对象识别与架构:求导和UML作业复杂度集中体现在低层基础对象的建立,电梯作业对每个线程内部实现复杂度较低、转而考察线程间的设计,JML算法与图结构分离形成的可拔插架构则介于上述两种之间。

不同层次对象的建立其实对应着今后工程开发中不同的层次,课程对我们了解不同层次的对象起着入门作用,还需在今后的学习工作中从基础做起,有所提升。

三、四个单元中测试理解与实践的演进

在编程开发中,功能正确性的实现是最基础也是最重要的,下面是我对于测试的一些理解:

  1. 测试对架构具有反作用,以测试导向开始构建程序,不仅好用,而且好测:测试有很多方法,我认为逻辑完备性测试最佳的当属单元测试,但是单元测试其实对类的设计提出了更高的要求,例如OO前两次作业开展单元测试相较于后两次作业就要困难一些(这当然不排除任务内容的客观差异)。因此,为了能够使程序能够良好适应各种测试手段,在程序架构设计阶段就必须将测试问题纳入考虑,只有测试便捷性的问题解决了,写出的程序才具备可维护性和可迭代性。
  2. 解决bug,避免bug,预防bug:预防-避免-解决是OS中关于进程死锁处理的三个层次,在我看来预防是上策,而针对OO的bug也同样适用:架构是最好的“debug”方法,对于这句话我在作业中有着深深的体会,通过架构设计+静态代码检查,绝大部分bug都能够被扼杀在摇篮之中,导致在单元测试和对拍测试阶段,抓出的bug永远多数在Junit文件和对拍器上……
  3. 单元测试很必要,要实现完全自动化和迭代化:针对每章作业,很多同学都实现了“民间数据生成器”用以测试程序,将自己程序提交这样的测试器固然有利于bug检查,但我认为其只能作为一个辅助和加强手段,基本的全面性测试一定要自己来做。
    1. 其一,今后的工程开发不可能200+名同学实现200+个完全一样的功能,标准参考少之又少,必须要在没有参考标准的情况下实现程序的正确性。
    2. 其二,今后Java中所提供的JUnit是支持单元测试完全自动化的工具,单元测试的特点就是能够根据实现的情况出入有针对性的数据,从而从逻辑上全面性地证明正确性,并且JUnit检测的全自动化使得测试的可重复性和作业的迭代性,从而让测试的雪球越滚越大。

上述三点是我在完成四次作业后对测试的一些理解与感悟,在测试实践的过程中由于一些主观因素和客观因素上述观点并不能都顾及。整体来说,第一章与第二章的作业的正确性判定较为容易,因此我借助其他同学的工具,实现相应的数据生成器和检测器,进行静态代码查错+黑箱测试;而在第三章和第四章,由于接口显著性的多任务倾向,我完全使用了静态代码查错+单元测试,经过后期对拍和强测,单元测试的强度非常好。

四、课程收获

OO这门课对我能力的提升是广泛而滋润型的,课程收获多方面而且实际,许多都内化在编程能力和每个章节的博客中,在此,我仅从宏观角度谈谈自己的收获。

  1. 自主学习,自主理解,自主实践:无参照、无上限是OO这门课的重要特点,作为凝结工程师常年开发经验和感悟的学科,OO课程的学习、实践和交流相比其他课程更具有开放性和探索性,也更代表将来研究与工作的实际环境。课堂上所讲内容远远不够,老师常常只能点到为止,课下要借助电子与纸质材料自主学习和理解,并将所学内容尽可能地尝试与使用(例如各类设计模式),才能将OO知识内化于自身。
  2. 架构设计
    1. 设计不容忽视:进过一学期OO学习,架构设计在我的程序设计中达到了巅峰的位置,从前期在编程前在草稿纸上简单地分类和画图,到后期UML模型、JML、接口规范等更遵循开发习惯的架构方式,优秀的架构从功能、迭代和测试都基于了潜在而巨大的帮助,虽然几次OO作业对培养架构能力是有限的,纵使今后有关知识都以忘却,但还需有时刻关注程序架构的意识
    2. 设计与实践不可分:当然,从另一方面也要知道——架构设计“空谈误国,实干兴邦”,在现阶段架构不可能一蹴而就,都是在编码实现、更低层次设计、可测试性等多方面因素反作用力下而逐渐走向优化的,因而要注意设计与实践的相结合。
    3. 设计与实践主次不可颠倒,注重关键实践细节的抽象与思考:虽有设计与实践的辩证关系,但是“因实践反作用力过大彻底毁掉了设计架构”这种主次颠倒的情况也是竭力避免的,就像在线程调度问题上,可能就因为线程间多类型异步通信缺少实现方法,从而颠覆整个设计架构。因此,也要注重对关键实践细节的思考与整合,避免因小失大。

五、课程建议

  1. 增加对异常处理的代码实现:课程对于异常处理基本没有考察,仅在后面两章有一些异常抛出的实现,建议通过编程实现对抛出异常的处理,增强同学们的编程鲁棒性。(例如,实现一个“中间层”的功能,既要对接顶层的内容,又要接受来自低层程序输出的内容,现阶段后两章我们实现的都是低层内容)。
  2. 减小互测房间中的人数:老师设计互测屋其中一个理由是想同学们阅读更多代码,但是“理想很丰满,现实很骨感”,7-8人的千行级代码在短短2-3天时间里完全阅读远远超出了我们的时间和能力范围,若想达到这样的目的,还需在互测规则上再加考虑。
  3. 研讨课有必要,但内容有待优化:本学期新开展了研讨课,里面许多内容很实在具体,对我影响还是很深,但是研讨课的自由发挥空间我认为过大,经过这学期研讨课的摸索,建议老师们为每次研讨课主题划定一定的范围和选题标准。
原文地址:https://www.cnblogs.com/sinoyou/p/11078416.html