前言
统一建模语言(英语:Unified Modeling Language,缩写 UML),是软件架构设计建模和规约的语言。
在UML系统开发中有三个主要的模型:
-
功能模型:从用户的角度展示系统的功能,包括用例图。
-
对象模型:采用对象,属性,操作,关联等概念展示系统的结构和基础,包括类别图、对象图。
-
动态模型:展现系统的内部行为。包括序列图,活动图,状态图。
本单元主要学习了以下关于UML的知识:
-
类图相关:类、属性及操作的表示,继承和实现、联系等关系的表示;
-
状态图相关:状态转移和状态的表示;
-
顺序图相关:对象和消息的表示
-
基于UML单元作业的分析
总的来说这一单元的作业较前几单元简单。完成这两次作业需要我们理解UML图中的数据是如何组织的,比如各条数据的parent id和id、source和target各代表什么含义。理解这些概念后,我们需要将输入来的数据解析存储,在查询时调用方法得到相应的数据。
第一次作业
我设计的第一次作业架构如上图所示,输入数据后,经Resolver的main方法调用功能实现类,进行存储原数据和查询等操作。值得注意的是,在存储原数据时我们就可以得到以下的一些有利于之后查询的数据:
-
类的数目
-
操作与所在类的联系
-
属性与所在类的联系
-
类(或接口)与类(或接口)之间的关联关系
-
继承关系
-
实现关系
存储这些数据后,在以后的查询操作中,我们避免了再次遍历搜索的步骤。
为实现不同界内数据隔离和查询时分工明确,我建立了ClassSta、AttrSta、OprSta、InterSta四个类分别管理有关类、属性、操作、接口的数据。
对于涉及到需要查询父类的操作,基于以下三点考虑:
-
没有关系环
-
中间数据可利用
-
空间开销较小
我使用了递归的方法解决。比如下面查找所有属性的例子
1 private HashSetfillAttr() { 2 if (attrIdFill) { return attrId; } 3 if (idSta.containsKey(dadId)) { 4 ClassSta cs = (ClassSta) idSta.get(dadId); 5 attrId.addAll(cs.fillAttr()); 6 } 7 attrIdFill = true; 8 return attrId; 9 }
首先没有继承环,因此递归不会产生死循环;其次当查找一个类类的所有属性时,我们必须先找到其父类的所有属性,通过递归可以“填充”上层类的属性池,最终一步步得到当前类的所有属性——而其他的bfs或者dfs等算法难以在寻找当前类的所有属性时“填充”父类属性池。
但是我这次的中测未过,更令我绝望的是我错在写错了一个属性(asso和attr都以a开头,并且idea关键词联想太贴心了)。de了好几天没de出来,同样是middle5,我以为我和别人错的一样,结果在意想不到的地方出了差错。
第二次作业
上图为第二次作业的架构图。这次作业在第一次作业的基础上,增加了以下数据和指令:
-
状态机相关:状态、转移的数据——个数查询、后继状态查询
-
顺序图相关:对象、消息的数据——个数查询、即来消息查询
对于数目的查询不再赘述,后继状态的查询与上次的查询方式有所改变,后继关系可能产生环,如图所示:
在这种情况下,盲目使用递归可能会陷入死循环,因此为保险起见,我使用了bfs算法来进行查找。
这次重量级的指令如下所示:
-
检查继承环的出现
-
检查多次继承的出现
对于第一条指令,我设计的处理为逐个检查类(或接口),找出所有的上级类(或接口),若上级类列表中含有当前类,则该当前类出现在继承环内。代码如下所示
public void checkForUml008() throws UmlRule008Exception { for (String cid : allCla) { if (cirInh(cid)) { cirItem.add((UmlClassOrInterface) idEle.get(cid)); } } ClassSta.InterSta is; for (String iid : allInt) { is = (ClassSta.InterSta) idSta.get(iid); if (is.isCirInh(iid)) { cirItem.add((UmlClassOrInterface) idEle.get(iid)); } } if (cirItem.size() > 0) { throw new UmlRule008Exception(cirItem); } }
查找上级类(或接口)时,同样采用了bfs算法。
对于第二条指令,我设计的处理步骤为:对于接口,检查不同的路线继承的上级接口,若有重合,则重复继承;对于类,若实现的所有接口中存在重复继承的接口,则该类重复继承。
OO理念理解与作业架构设计
在大一期间学习了数据结构这门课,对编程有了有一个初步的了解。记得当时的C语言作业仅仅是按照要求实现过程即可。对于写过的数据结构作业,我认为与OO课程最大的区别是只用一个c文件实现。固然一部分原因是我们的作业要求实现的功能单一,代码量不长。另一部分原因我认为就是面向过程编程和面向对象编程的区别了。下面是我对于面向对象和面向过程的一些浅薄的理解。
可能我们初试面向对象的思想时看到诸如此类的文章就会猜到接下来会将一些什么东西——面向对象就是“万物皆对象”,把想要实现的功能看作对象的动作,以描述对象为核心,譬如对于面向过程的“要干什么”理解为“谁会干什么”。这些大致相似的讲解直观地说出了以面向对象的思想我们应该怎么去编程。但是为什么要这样做呢?
我认为可以概括为一个词:界限。在有多个任务需求的时候,界显得尤为重要。单线程的大脑难以应对繁杂的事务,因此产生的协作。不同的企业单位处理不同的事,这时就需要我们划分处理者到不同的单位。每个企业单位都需要有管理者负责处理业务,在面向对象概念里,我们称该管理者为对象。这样一来,我们在处理多种任务时,把不同类型的人物分给相应的对象即可,比之面向过程中稍显麻烦的调用有更清晰的思路。
有了面向对象之后,我们发现这个思想是一个好东西。首先,面向对象有利于实现多种功能的程序的编写。由于面向对象的界限,我们可以清晰地调派任务。这对于工程化设计是非常有利的,面向对象思想能够更好地把握整体的规划。其次,面向对象有利于架构的设计,在设计架构时,我们不必先考虑可能会很繁杂的实现方法,只需指定一些类可实现这些任务即可,有利于我们对整体框架的设计。由此可见,面向对象思想是一种自上而下的思想。最后,面向对象更符合我们的思考习惯,我们在思考时遇到较为复杂的问题时难以一时找出解决思路,自上而下地剖析有利于我们思考的进行,实现了由易到难的转变,避开了跳跃式的思维方式,有利于工程化分析问题。
学习了面向对象编成后,我对作业的架构有了一个更深的理解。对于一个作业,想要迅速确立方向的最好方法就是建立架构,建立好架构后只需填充细节,这样便破除了束手无策的境地。
回到一开始所述区别,面向过程我只想用一些函数实现我要的功能,整个c代码在编程的联系上是一个模糊的整体,而OO作业的代码把不同的任务区分地很清楚,细化了面向过程的粒度,增强了面向过程的部分的地位。
对于程序测试的理解
由于本人水平所限,当前我对程序测试的体验还停留在debug阶段,但是作业既然要求写这些内容了,我就发挥一下想象力写一些关于程序测试的理解,也希望自己有足够的耐心和主观能动性用到这些理解。
程序测试大概可分为三个阶段:
-
基础功能测试
-
边界条件测试
-
性能测试
基础功能测试即测试程序是否实现了已有的功能,毕竟这是程序设计的目的。边界条件测试是测试一些特殊的情况下程序的正确性。在编写程序时,脑海中大致思索方向是概括的、显著的现象,容易遗漏一些边界条件下的特殊情况,如若未考虑到可能会导致正确性的丢失。最后的阶段测试为性能的测试,即测试程序的响应时间,同时也是程序员个人水平的体现。
除了以上几点测试,同时也要注重程序鲁棒性的测试,即抗压能力要强,对于危险情况要有对应的处理措施,一个能抗能打的程序才是真正意义上的好程序。
自己的最后几次作业的bug比较多,回顾自己的作业历程,除了自身的编程经验和细心程度不够而导致的拼写错误,对边界条件的测试做的不够好。首先自己对测试的热度不够,仅停留在测试的样例和求助同学遇到的错误情况。因此有三次中测都未过,自己的测试力度太小了,以后的作业希望自己早点写完、积极debug。同时多构造边界条件的测试,掌握构造样例的方法,拒绝构造重复样例的无用功。(或许应该用个小本本记下样例的测试点)
课程收获
学习面向对象程序设计这门课我收获了许多。
首先,显而易见,对面向对象的编程思想有了一个深刻的认识。原来只觉得面向对象这个名词“神神呼呼”的,编程怎么还与“对象”说不上来是什么东西的名词扯在一起。面向对象程序设计方法是尽可能模拟人类的思维方式,使得软件的开发方法与过程尽可能接近人类认识世界、解决现实问题的方法和过程,也即使得描述问题的问题空间与问题的解决方案空间在结构上尽可能一致,把客观世界中的实体抽象为问题域中的对象。学习之后对我的编程思想起到了巨大的引导作用。在原来的编程我会直接去想用怎样的方法去实现,现在我会先考虑程序的架构,一步一步分析,将问题由大化小去解决问题。
其次,我学到了一门语言。学习java之后感觉真的是太方便了,比如在c语言的数组的一些增删改查操作还得自己实现,在java中直接用容器实现。除了java外还了解了关于JML、UML的相关的知识。
最后,这门课给了我许多经验,启发了我的一些学习思路、方法。记得第一单元最后一次作业,一个bug困扰了我好多天,到最后也没能de出来,不过那一次的作业延迟了,又是两个小时也没有任何进展,就在几乎放弃的边缘被我de了出来,当时的欣喜不次于买彩票中了大奖(因为确实是蒙的一个测试用例)。那次作业给了我两个启发:不到最后一刻永远不要放弃;你的debug水平有待提高。
总之,感谢OO这门课,给了我许多挑战和或刺激或沉重的体验,提升、锻炼了我的能力。我会继续努力前行的!感谢课程组精心设计的这门课,感谢老师和助教们的付出!
一些小建议
-
bug修复的标准需要改善
-
上机前希望给一个程序的例子,方便熟悉上机的内容
-
关于JML和UML希望举一些工程应用上的例子