BUAA_OO_U4_Summary

BUAA_OO_U4_Summary

一 / 架构设计

1.0> 题目解析

实现UML类图的分析。

1.1> HW13

1.1.1> 关于UML

从JML到UML,对于从模型到实现的能力训练,此单元从上一单元的Java Modeling Language升级到了Unified Modeling Language——统一建模语言。它是通用的,不是针对某一种语言而制,

截止UML2.0,一共有13种图模型。在pre中便已让我们接触了类图,在接下来两个单元的总结中本人也对着代码绘制了相应类图(但其实老师强调了多次,这是一个错误的顺序,应该先有类图,再对照着类图形成代码,所以这个错误的顺序只能更加凸显本人架构能力的脆弱,只能边写边想从而产生一些不必要的重构)。第二个单元在处理线程问题时也用到了顺序图。在这个单元中新增了对状态图的认识。

  • 类图:描述系统中类的静态结构。
  • 顺序图:对象之间的动态合作关系,强调对象发送消息的顺序,同时显示对象之间的交互
  • 状态图:是描述状态到状态控制流,常用于动态特性建模

1.1.2> 关于类图

这里借用第四单元手册里面的一张图。它已经很清晰的展示了类图里面的元素及其结构。本人在编程时也采用的此结构进行的所需类的设计,可见下面的代码架构部分。

1.1.3> 找到全部父接口

这个单元说难也不难,说简单也不简单(至少令本蒟蒻感到比较头疼),一个是读不懂指导书,另一个就是拿不准应该怎么实现。最后本人的所有功能都是能跑但是暴力的做法,没有追求任何时间上的优化了(因为实在来不及优化了)。但是在找父接口的地方,如果每次都递归到曾曾…曾祖父去,未免太浪费时间了,所以采取记忆化搜索的方式。

按理说,记忆化搜索很好写,即如果已经搜索过这个接口,那么这个接口的全部父接口都应该已经被存储到这个接口里了,直接拿来用即可,否则继续递归下去。但是对于一个java数据结构新手来说,遇到Null Pointer Exception已经是家常便饭了,这种时候有两种解决方式,一个是单步调试,看是哪个语句报了错,另一个是利用idea的流调功能,其实和单步调试很相似但是不用自己去运行了。

1.1.4> 代码构架

MyImplementation:用于实现官方包的接口,但是在这一次作业中,本人把大量的处理代码都塞在了这个类中,导致行数极多,翻起来也非常复杂
MyClass:通过组合封装了一个UmlClass,用于处理类之间的关系(父类、子类、联系等)和类自身的属性(拥有的属性、方法等)
MyInterface:通过组合封装了一个UmlInterface,用于处理接口之间的关系(泛化等)和接口自身的属性(拥有的方法)
MyOperation:通过组合封装了一个UmlOperation
MyType:用于判断一个UmlParameter的类型是否合法,在这里本人卡了很久,因为迟迟没有发现怎么使用官方包进行类型的判断,直到发现需要先转型然后才能用NameType中的类型进行字符串比较。

1.2> HW14

新增对状态图和顺序图的分析。

1.2.1> 关于状态图

再次从手册里截一张图。

状态图相对来说比较好理解,一方面原因是它长得就是一个状态机(在上个学期已经被折磨过了),另一方面是第四单元手册中对于状态图的讲解非常详细,还有原因就是其中涉及的结构相较来说简单一些。如上图右上角所示,状态机表示的就是从一个状态到下一个状态,需要什么条件诱发,诱发的结果是什么。

1.2.2> 关于顺序图

顺序图在第二单元便已经接触过了,但是当时画的那幅图一言难尽,首先是混乱,其次是用错了很多箭头。通过这次的手册本人也深刻意识到了这两点。

图片依旧来自于第四单元手册。

这个结构图看起来就更加清晰明了了。但是很有趣的一点是,UmlAttribute不仅在类图里作为class的属性出现,也可以作为状态图中表示协同行为的属性成员,每个UmlLifeline都关联到一个UmlAttribute ,也就是对应了一个具体的对象。

1.2.3> 代码构架

这张图以MyImplementation类为中心,左上是状态图,左下是顺序图,右侧是类图的元素们。

MyImplementation:用于实现官方包的接口,这次作业没有新建utils类,并把这个类控制在了刚好500行的位置

MyClass:通过组合封装了一个UmlClass,用于处理类之间的关系(父类、子类、联系等)和类自身的属性(拥有的属性、方法等)
MyInterface:通过组合封装了一个UmlInterface,用于处理接口之间的关系(泛化等)和接口自身的属性(拥有的方法)
MyOperation:通过组合封装了一个UmlOperation
MyType:用于判断一个UmlParameter的类型是否合法

MyStateMachine:通过组合封装了一个UmlStateMachine,用于保存当前状态模型最顶层的结构。但是本单元作业保证了每个StateMachine下面只有一个Region,所以这个类可有可无,本人只是想让自己代码的类图看起来完整一点
MyRegion:通过组合封装了一个UmlRegion,里面存储了所有的state和所有的transition,是一个画布的存在
MyState:通过组合封装了UmlStateUmlPseudostateUmlFinalState,并设置变量记录状态的类型,存储从此状态迁出的trasition集合

MyCollaboration:通过组合封装了UmlCollaboration,存储所有的attributeinteraction
MyInteraction:通过组合封装了UmlInteraction,存储所有的消息和生命线
MyLifeLine:通过组合封装了UmlLifeline

1.3> HW15

1.3.1> 迭代开发

对类图、状态图、顺序图中的异常进行判断。

1.3.2> 读不懂指导书

对于这样一条R002的规则,给出的解释如下:“针对类图中的类(UMLClass),其成员属性(UMLAttribute)和关联的另一端所连接的 UMLAssociationEnd 这两者构成的整体中,不能有重名的成员。”

拆词能看出来,他让我找成员属性+关联+另一端的连接+整体+重名+成员。成员属性和成员是一个东西吗?我只知道属性是属性,UMLAttribute可以被当做是属性,现在这个解释告诉我它也教成员属性,那成员究竟指什么?虽然最后的事实证明,属性=成员=成员属性。整体又是什么?自己这个类中的这个属性和关联的另一个类或接口的全部属性,以及两个UmlAssociationEnd构成了这个整体的入侵时成员,所以只需要再这个里面找重名的即可。但是理解这个概念远比我写完这次作业所需时间长的久——现在看懂了,便有亿点体会不到当时那种焦虑无助的感受了,但指导书中的各种文字真的充满了陌生与挣扎。

1.3.3> 代码构架

这个是完整版。这张图以readDiagram类为中心,左上是类图,左下是顺序图,右侧是状态图图的元素们。

MyImplementation:用于实现官方包的接口,新建的Utils类分走了很多代码

ReadDiagram:工具类,用于读入

MyClass:通过组合封装了一个UmlClass,用于处理类之间的关系(父类、子类、联系等)和类自身的属性(拥有的属性、方法等)
MyInterface:通过组合封装了一个UmlInterface,用于处理接口之间的关系(泛化等)和接口自身的属性(拥有的方法)
MyOperation:通过组合封装了一个UmlOperation
MyAssociation:通过组合封装了一个UmlAssociation,用于保存关联关系的两端
MyElement:庆祝一下蒟蒻终于使用了接口这件事。其实没有必要,这个只是作为统一MyClassMyInterface,便于跑dfs和bfs处理异常的时候用
MyType:用于判断一个UmlParameter的类型是否合法

MyStateMachine:通过组合封装了一个UmlStateMachine,用于保存当前状态模型最顶层的结构。但是本单元作业保证了每个StateMachine下面只有一个Region,所以这个类可有可无,本人只是想让自己代码的类图看起来完整一点
MyRegion:通过组合封装了一个UmlRegion,里面存储了所有的state和所有的transition,是一个画布的存在
MyState:通过组合封装了UmlStateUmlPseudostateUmlFinalState,并设置变量记录状态的类型,存储从此状态迁出的trasition集合
MyTransition:通过组合封装了UmlTransition,存储其上的所有event

MyCollaboration:通过组合封装了UmlCollaboration,存储所有的attributeinteraction
MyInteraction:通过组合封装了UmlInteraction,存储所有的消息和生命线
MyLifeLine:通过组合封装了UmlLifeline

稍微更清晰但简略的版本如上图所示。本想归类更明显一点,奈何这个连线动起来实在是太麻烦。但是大致可以看出来,从左往右分别为类图、状态图和顺序图的架构。

二 / 架构设计思维及OO方法理解的演进

2.1> 架构设计

尽管每个单元各有各的侧重点,但是在所有的博客中,必要的环节都是架构分析。它是一个抽象,一个对于我需要实现的任务目标的抽象,不考虑实现细节,不需要考虑每个对象如何工作,只考虑需要什么对象,他们各自做什么的过程。就像一个大老板,安排好任务,怎么实现是劳动力的事情——这就很面向对象了。Uml图是很好的一个表达方式,但可惜本人有此意识而无此行动,每次都是写完代码才着手绘制UML图,在其之前都只是在脑子中构想我需要怎么化解这个任务。

第一单元的目标是解析表达式,所以对象就是表达式。架构设计体现在把这些表达式分类,分成多项式、单项式等的抽象,然后进行处理。

第二单元的目标是让电梯安全的运行,涉及到的对象有电梯、请求、生产着消费者模型中的托盘等,在套用改模型的前提下需要考虑生产者和消费者都是什么的问题,其余更多的线程安全实现不在架构考虑范围内。

第三单元跟着JML走,通过JML告诉我们的社交网络中基本单位(人、小组)和需要实现的查询方法构建整个社交网络的结构,是架构非常清晰的一个单元。

第四单元分析UML,架构同样很清晰,于是直接采用了UML类图的架构。三种不同的图示类型也让本人清晰的了解了三种处理不同问题的架构应该是怎样的,只不过这个架构是从比确定问题中的对象是什么而言更抽象、更高的角度,来看待一个问题可以被划分成类、接口或是各个状态、各个生命线的模样的方法。

2.2> oo方法理解

第一单元主要聚焦点有点跑偏到实现方面了,也就是递归下降。对于面向对象的直观体验是从表达式中抽象出一个最小的不考虑它内部如何处理的单位,然后不断合并为新的可处理的对象,直至合并至整个表达式。

第二单元学习了不少设计模式,这是面向对象编程的基础。面向对象注重行为,设计模式就告诉你行为的蓝图。而设计模式的五大基本原则单一职责开闭(对扩展开放,对修改封闭)、依赖倒置(细节依赖于接口,针对接口编程,而不针对实现编程)、接口隔离(使用多个专门的接口)、里氏替换原则(子类必须能够替换掉它们的父类)在后续的作业中,让本人的架构设计和面向对象思想变得更清晰了一些,特别是帮助本人降低类与类之间的耦合度方面,以及降低复杂度,让代码变得易于调试减少错误方面都有着很大的提升。

第三单元中,JML的规格约束让本人更加深入的理解了面向对象的意义,即利用的高度抽象,清晰明确的展现需求,不考虑步骤而关注功能,不注重过程而关注行为的解决问题视角。

第四单元中,UML这样的面向对象的建模语言,让本人在实现的同时回顾了整个oo中用到的类、接口等元素以及继承、泛化、组合、依赖、实现、关联这六种关系,更加深刻的体会了面向对象的建模方式。

三 / 测试理解与实践的演进

其实本人只在一三两个单元做了测试程序的编写(包括评测和对拍),二四单元没有写评测机。从错误的数量来看,明显没有进行自行评测的两个单元得分更低,所以如果时间允许,多做测试

实践方面,在第一单元作业中,本人学习了如何使用python的subprocess模块中的popen,第一单元采用通过开启pipe来和程序交互,第三单元输入量较大,输出量也大,所以如果输入输出都使用pipe进行的话,管道会炸掉,所以改用输出至文件的方式。

如第一单元:

process = subprocess.Popen(['java', '-jar', jarDir], stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = process.communicate(strInput.encode())

ret = []
ret += stdout.decode().split('\n')
process.kill()

第三单元:

process = subprocess.Popen(['java', '-jar', jarName], stdin=PIPE, stdout=open(outputFile, 'w'), stderr=PIPE)

for ln in strInput:
	process.stdin.write(ln.encode())
process.stdin.close()

process.communicate()
process.wait()
process.kill()

这个做法一直沿用了下去。对于每条指令的测试,无论一三单元,本人都是通过一个个单独编写的函数进行的,同时再用一个函数来生成所有的输入,这样可以便捷的更改测试数据。

如:

def add_person():
	···
def add_relation():
	···
def query_value():
	···
def getInput():
    for j in range(5):
        add_person()
    for j in range(10):
        add_relation()
    ···

从测试的理解方面,两个单元中均采用了手动构造+在给定范围内的随机测试,从测试范围来看,均有从单元测试到复杂测试的过程。其中第一单元中“单元”的划分为每个运算符,针对它的测试本人做的极为有限,基本是手动输入一些可能发生的复杂情况(比如-1**0)。而第三单元中这个“单元”的概念被我放大到这一段干了同类型事情的指令,比如造人+造组+组里加人+造红包信息+发红包+查询钱数诸如这样的组合。但这样的测试也需要注意顺序,比如先测好基本的功能,如group加人删人是否正常,再进行一个个功能单元的测试,如收发红包发表情发群公告等,最后再将其融合在一起,进行大杂烩的测试。同时,由于完全随机的效率低下,在第一单元中就遇到了在2k次中仅出现一次错误的情况,所以在第三单元中本人也改变了随机测试的思路,变为半随机的样子——手动控制人的id,随机其他数据。这样明显提高了数据的强度,在自测和互测中都起到了非常好的作用。

在互测中,本人也学习了阅读他人代码并发现错误的能力,虽然这种错误仅限于字符串处理或者一些程设方面的错误,其他的问题还是需要借助评测机的帮助才能被看出来。

所以总结来看,本人通过四个单元的评测,学习了测试的步骤和方面,并在自己的实践中不断拓展测试的方面,提高数据的有效性,向覆盖率高的测试方向发展。

五 / 课程收获

pre:进行了java的入门,并认识了非常典型的面向对象的例子,通过实践体会了继承、实现、关联等关系,同时认识了UML类图这样的架构表述方式。

第一单元:运用pre中学习的基本知识,对表达式进行抽象,利用在training里学到的递归下降方法实现表达式的解析。

第二单元:通过对电梯的安排,了解线程安全的实现方式(加锁,加读写锁,加sychronized修饰符,采用阻塞对了等),学习不同的设计模式(如创建型模式中的单例模式、工厂方法模式,结构型模式模型中的组合模式,行为性模式中的观察者模式,以及在23中基本模式之外的生产者消费者模式)。对架构能力的要求为四个单元中最高。

第三单元:阅读JML,学习JML这种语言的语法和优势,在构建社交网络的过程中复习图论的算法并在java中编写数据结构,学习java中异常的抛出与处理。JML具有非常明确清晰的需求描述,使得本人可以快速得到架构与实现方法。

第四单元:将modeling language从Jml上升至Uml,处理Uml类图、顺序图和状态图三种常用绘图方式的解析方法,更加深刻的体会如何拆分一个任务,并安排需要的对象。

六 / 改进建议

  1. 希望可以在每次上机后给出上机测试的答案,虽然这种事情也可以通过问同学解决,但如果有更便捷的,能让所有人都获得答案的方式,看起来会更方便一些。这样本人才能更好的意识到自己在某些地方对代码理解上的偏差,特别是后面的三四两个单元,不是程序能跑就说明写的没有问题,而且基本都是填空题,如果没有标准答案的话,容易产生一些误解。

  2. 研讨课的分享可以摆脱“上一次作业我是如何实现的”这种话题(除非是非常精妙特殊的实现方式),毕竟大家都已经写完了,而且有互测的代码进行观看,在组内讨论的环节进行分享就够了,再进行这样的讲解感觉有点耽误了研讨课的时间。

  3. 在每个单元第一次作业之前发布一点自愿思考的小任务,比如在pre阶段接触一点递归下降的思路,在第一单元博客时写一些比training中的限制多一些的生产-消费者模型等等,当然一定采取自愿的形式,稍微降低一点每个单元第一次作业的难度,否则经常到周四的上机后才能意识到应该怎么写,感觉时间有点紧张(不过…可能菜是原罪吧)。

  4. 第四单元的指导书中,有一些文字可能还可以斟酌一下,因为经过第三单元的JML之后,感觉文字表述的二义性还是有点明显的,阅读充满文字的要求突然觉得陌生了起来(当然,也有可能是本人理解能力的问题)。

  5. 讨论区有时翻起来比较混乱,问题和助教回答混在一起,不知能否改进为每个回答都显示在问题紧下方的形式,这样不容易看漏也比较方便阅读。

posted @ 2022-06-29 13:13  emilyu  阅读(26)  评论(0编辑  收藏  举报