OO第四单元总结及学期总结
第四单元综述
第四单元作业主要是利用java面向对象处理对于统一建模语言UML的结构分析问题,涉及到对UML基本概念的理解。类图、状态图和顺序图中元素的关系,如下图所示。
本单元的作业实际需要实现官方接口UserApi。具体而言,需要根据输入内容搭建UML图对应的虚拟模型(用类、接口等和它们的关系表示),再利用该虚拟模型对输入内容所对应的实际UML模型进行分析。
第十三次作业
只涉及到对类图的分析,可以直接建立MyImplementation类实现UserApi接口的方法。
但更多的是涉及到类图层次结构的分级以及据此建立相应的模型。
对于类图,我们可以分步加入相应的元素:
-
加入Class和Interface
-
加入Attribute、Generalization、InterfaceRealization、Operation、AssociationEnd
-
加入Association、Parameter
uml类图
主要类的含义作用介绍
-
MyImplement用于根据输入元素
UmlElement[]
构建相应的MyClass、MyInterface等,实现官方定义接口UserApi中的方法get~,实际上相当于处理上层输入与底层实现工作的一个中枢; -
MyClass一个对象代表一个UmlClass模块,但增加了子类、父类、含有的操作等属性,访问继承深度、访问有哪些子类等方法;
-
MyInterface一个对象代表一个UmlInterface模块;
-
Associated为了给MyClass、MyInterface建立一个共同实现的接口,便于调用使用;
-
MyOperation一个对象代表一个UmlOperation;
-
其他元素并未自定义模块,因为它们设计到的方法很少,可以保存在以上定义的类中,作为它们的一部分存在。
其他说明
-
使用一个id2elem的HashMap<String,UmlElement>用于存储所有元素id和元素本身的对应关系,这样在加入Association这样涉及到两个元素之间关系的元素,就可以通过id直接找到相应的element元素。
-
以空间换时间的方法。本次实验的访问结构在传入uml图确定后就确定了,意味着我们可以在构建虚拟模型的过程中就将要查询的对象存储起来,比如在加入继承的过程中,就可以将子类对象在其父类中用一个collection存储起来(subClasses),当访问getSubClassCount时,直接调用相应MyClass的getClassCount方法,返回
subClasses.size()
即可。
第十四次作业
本次作业在第十三次作业上扩展了一些对于顺序图和状态图的访问。所以需要扩建虚拟模型。
根据【第四单元综述】部分展示的Uml关系图可知:
首先,加入与Model同一级的Collaboration和StateMachine。
然后,逐层加入其他各级元素。(主要根据Parent的类型,Parent先加入)
涉及到关系的如Message,Transition等可以在其联系的实体加入后再加入,方便将其直接存储在实体里面,便于后序查询。
对于AssociationEnd,由于其reference是UmlClass|UmlInterface,所以可以再Class和Interface加入后再处理,之后再加入Association就可以通过AssociationEnd联系到相应的UmlClass | UmlInterface。
uml类图
其他说明
-
可以构造类专门处理相应类型Diagram的解析,它们之间通过id2elem进行相互通信。
private HashMap<String, Object> id2elem = new HashMap<>();
private ClassDiagram classDiagram = new ClassDiagram(id2elem);
private StateDiagram stateDiagram = new StateDiagram(id2elem);
private SequenceDiagram sequenceDiagram = new SequenceDiagram(id2elem);
这样在MyImplement构造函数中就只需要将元素进行分类派发,可以有效缩短代码长度。
for (UmlElement e : elements) {
if (e instanceof UmlClass) {
classDiagram.parseUmlClass(e);
}
/*do something else*/
}
-
访问某状态是否是关键状态:记录出所有从initial State到Final State的路径,判断该元素是否是所有路径所共有的元素。
第十五次作业
增加了END_OF_MODEL后的规则检查要求。(checkForUml00x)
其他说明
循环继承:即意味着有向图中出现了环。我们可以使用深度搜索得到路径,判断有没有可能在路径中出现重复节点,如果可能则意味着存在循环继承的问题。
架构设计思维及OO方法理解的演进
第一单元 表达式解析
第一单元的作业主要通过表达式解析,让我们初步熟悉面向对象的思维方法。学习如何划分类结构,如何根据需求构建相应的类。
由于是第一次作业在第一次作业就面对表达式展开建立模块架构,无所适从,完全不知道如何设置类,如何将有效的方法属性封装在一个类里面。
通过课上学习,我才知道可以通过利用数据结构二叉树表达式分解的方法,但不知道应该如何设置类。直到做了看了第一次课上实验的代码,让我找到了救命稻草。
-
HW_1:
-
目的:去掉含有+,-,*,**的表达式中的括号。
-
解决方案:构建Add、Sub、Mul、Bracket类。
-
Parser解析按照运算优先级依次处理Add/Sub、Mul、Pow(转换成连乘)、Bracket(Sin/Cos)、Num/Alpha处理。
-
Sub表达式前为负号,转换为Bracket类,拆分成项后变号。
-
Mul表达式转换为Bracket类,拆分成项,依次相乘。
-
-
HW_2:
-
目的:在第一次作业基础上增加sin(),cos(),sum()函数,自定义函数。
-
解决方案:补充sin()、cos(),sum()类。
-
自定义函数及Sum函数预先处理,将函数名和表达式存入HashMap中,预先进行表达式自定义函数及Sum函数处理,以实际参数进行字符串替换掉形式参数再放到表达式中进行解析。
-
-
HW_3:
-
目的:在第二次作业的基础上增加了嵌套函数处理问题
-
解决方案:Parser类中增加自定义函数解析方法,采用递归处理方法处理嵌套函数。
-
Parser解析按照运算优先级依次处理Add/Sub、Mul、Pow(转换成连乘)、Bracket(Sin/Cos/Sum/Func)、Num/Alpha处理。
-
第一单元的建模虽然实现了单元作业的要求,但是还是更偏向于数据结构面向过程的方法,还没有能够完全掌握面向对象的思维方法,比如可以明显看到Parser类,完全就是一个大的方法类,里面都是各种各样的方法,都没有属于它自身的有效属性,需要调用某种方法的时候就新建一个Parser类,调用里面的方法。Parser.parse()使用了大量if...else..嵌套结构,方法认知复杂度、结构复杂度、模块设计复杂度、圈复杂度都较高,并且随着作业的迭代开发复杂度有所升高。
实际上可以通过表达式分解递归下降的方法进行简化。
第二单元 电梯
第二单元我们在第一单元单一进程面向对象的设计基础之上,进一步学习和练习使用了多进程。
目标是模拟多线程实时电梯系统,熟悉线程的创建、运行等基本操作。
这是学习难度最大的一个单元因为要从头学多线程,理解进程思维和设计模式。
-
HW_5:
-
每座一台上下行电梯
-
解决方法:生产者消费者模型
-
TestMain接收乘客请求信息相当于生产者、RequestQueue相当于生产者消费者之间的盘子。Elevator从RequestQueue中获取请求并处理请求。
-
ALS调度
-
对于线程安全有了初步理解
-
-
HW_6:
-
新增楼座与楼座之间移动的环形电梯,每个楼层可以对应多台环形电梯
-
解决方法:输入:单例模式(只有一个实例)
输入+控制器(controller)+电梯(Elevator):生产者-消费者模式
-
另设TestInput2进程用于输入请求分配,设置两种Controller(Building,Floor)用于控制电梯运行,另设输出线程保证输出线程安全。
-
-
HW_7:
-
横向电梯只能在部分楼座开门,乘客需要中转,电梯可定制
-
解决方法:新增流水线模型
一个乘客的请求可以分成三个部分,纵向运动+中转楼层横向运动+纵向运动,流水线派发一次处理
-
注意轮询、死锁问题
-
这一单元面向对象的思维得到进一步训练,能够有意识地划分类,并根据设计模式,设计各类的功能。
第三单元 JML
-
了解规格化描述语言JML的基本语法和语义;
-
根据JML给出的规格编写Java代码,实现契约式编程。
-
考察算法的应用,不光要在结果上满足需求,还要在性能上满足需求
第四单元 UML
-
学习统一建模语言UML,
-
学习类图、顺序图、状态图的概念、作用、画法,各元素之间的关系
-
能够根据目标设计面向对象架构
-
能够根据需要实现目标方法
-
能够较熟练的掌握各类之间的关系
-
能够较熟练的应用各种语法结构,实现性能优化
测试理解与实践的演进
第一单元
第一单元输出结果是一定的,可以使用黑盒测试,也就是根据题目要求生成测试数据,再将代码获得的结果与正确结果(实际手动计算或通过其他方法得到的结果)进行对比。需要注意考虑覆盖率的问题,也就是尽量使得各种分支语句的情况都能够测试到。我第一单元被hack的几次就是由于情况考虑不全面。
也可以使用白盒测试,也就是阅读代码分析,但面临的困难是在没有代码解释的情况下理解全部代码耗时较长,而且不易发现bug。
第二单元
由于进程并行,可能会出现bug无法复现的问题。采用黑盒测试,用数据生成器投入大量数据进行对拍也可能无法找出bug。可以针对题意分析可能产生的逻辑漏洞,手动构造相应的数据进行测试(比如同一时间在同一位置投入大量乘客请求,或者在数据要求范围内投入尽可能多的数据看代码是否会超出规定时间等)
第三单元
这次有了JUnit架构这一工具,而且每个方法给出了明确的先验条件、后验条件和不变式,在自行进行代码检查的时候可以单独对一个方法进行测试,便于更快速精准的定位问题,可以assert判断各条件是否满足。
互测的时候还是使用了生成大量数据对拍的形式。(比如生成大量qgvs去卡tle)
第四单元
可以有意识根据题目要求的方法查询可能出现的所有情况,构建相应的UML图,进行覆盖性测试。
课程收获
-
学习熟悉JAVA语言,能够根据需求实现类继承、接口继承等
-
学会git进行简单的代码管理
-
了解多线程运作机制
-
了解并应用多种设计模式
-
复习图论、堆栈、prim算法、dfs等数据结构知识
-
-
学习JML语法,实现契约式设计
-
学习UML统一建模语言,能够画图并理解各元素之间的关系
-
建立了面向对象的思维,摆脱了面向过程的单一设计模式
-
能够进行白盒或黑盒测试
课程建议
-
可以为有兴趣的同学给出测评机搭建教程,培养自主测试能力。我自己造的测评机数据往往不够理想(有时候连投放都不符合标准),不够全面(全覆盖),最后不得不手动构造。
-
感觉第一单元上手相对困难,即使是在假期做了pre,但要能够自主建立模块结构还存在面向对象概念理解的问题,导致不知道如何建立类和类之间的有效关系,希望能适当增加一下这部分的提前训练。
-
适当增加一下中测得代码强度,希望能在强测互测之前发现并及时修改bug,以防中测一次过、强侧互测被hack烂。
课程感受