BUAA-OO-第四单元总结

BUAA-OO-第四单元总结

一、第四单元架构设计

以第三次作业为例,整个架构可以大致分为三个模块,即类图、顺序图和状态图。

类图

 

类图中主要有三类节点,class、interface和operation。Uml图中的Mould类是ClassNode和InterfaceNode的父类,结合了二者的共有部分,如拥有的属性和方法、ID、name等成员属性,以及一些检查合法性和处理指令的方法。Mould类中存储属性、方法、关联端点,OperationNode类中则存储该方法包含的所有参数。这样形成层次分明的关系,在执行指令或检查合法性时也能层层往下调用。

顺序图

 

顺序图是比较简单的链型关系,体现了顺序图层次关系,即顺序图下存储所有的Interaction,Interaction存储其包含的Lifeline,而分析指令要求,Lifeline只用存储found和lost消息即可。在执行指令时同样类似于类图,顶层类使用负责调用下一层类的方法,具体实现则只需要交给下一层类。

状态图

 

状态图的设计从结构上非常类似于顺序图,StateGraph存储所有的状态图节点,StateMachineNode存储RegionNode(实际上如果只考虑本单元作业,这个类其实没有必要设计),RegionNode下包含所有该状态图中的所有状态节点,同时每个状态节点又用邻接表的形式存储TransitionNode和与之相连的状态节点。

数据解析方式

从设计上不难发现,本单元作业的数据有很明显的层次关系,这也就要求我们要按层次进行数据解析。具体而言我根据自己的设计分为五层解析。

  1. 解析class, interface, interaction, stateMachine, collaboration

  2. 解析operation, associationEnd, generalization, realization, attribute, lifeline, endpoint, region

  3. 解析parameter, message, state, association

  4. 解析transition

  5. 解析event

值得一提的是,尽管从层次上来看,association的层级应该高于associationEnd。但由于本次作业中关注的不是和类同一端的端点,而是另一端的端点,因此笔者采用了先解析associationEnd,再解析association的方法。

指令执行过程

由前文可知本单元架构设计具有明显的层次性,这样的好处是在执行指令或检查合法性时,其方法的调用过程也有极强的层次感,这样每一层只用处理这一层该处理的事,剩下的交给下一层即可,这样的结果是逻辑清晰,容易定位错误。这里以状态图的指令2(给定状态机模型和其中的一个状态,判断其是否是关键状态)为例,简单介绍调用过程。

 1 /* MyImplementation 直接调用StateGraph的方法 */
 2 public boolean getStateIsCriticalPoint(String stateMachineName, String stateName)
 3             throws StateMachineNotFoundException, StateMachineDuplicatedException,
 4             StateNotFoundException, StateDuplicatedException {
 5         return stateGraph.getStateIsCriticalPoint(stateMachineName, stateName);
 6     }
 7 
 8 /* StateGraph 简单判断异常后调用StateMachineNode的方法 */
 9 public boolean getStateIsCriticalPoint(String stateMachineName, String stateName)
10             throws StateMachineNotFoundException, StateMachineDuplicatedException,
11             StateNotFoundException, StateDuplicatedException {
12    ArrayList<StateMachineNode> stateMachines = name2StateMachine.get(stateMachineName);
13         if (stateMachines == null) {
14             throw new StateMachineNotFoundException(stateMachineName);
15         }
16         else if (stateMachines.size() > 1) {
17             throw new StateMachineDuplicatedException(stateMachineName);
18         } else {
19             return stateMachines.get(0).getStateIsCriticalPoint(stateName);
20         }
21     }
22 
23 /* StateMachineNode 直接调用RegionNode的方法 */
24 public boolean getStateIsCriticalPoint(String stateName)
25             throws StateNotFoundException, StateDuplicatedException {
26         return region.getStateIsCriticalPoint(stateName);
27     }
28 
29 /* RegionNode 判断异常后,调用StateNode的方法 */
30 public boolean getStateIsCriticalPoint(String stateName)
31             throws StateNotFoundException, StateDuplicatedException {
32         ArrayList<StateNode> states = name2StateNode.get(stateName);
33         if (states == null) {
34             throw new StateNotFoundException(machineName, stateName);
35         }
36         else if (states.size() > 1) {
37             throw new StateDuplicatedException(machineName, stateName);
38         }
39         else {
40             if (isConnect == 0) {
41                 isConnect = origin.isConnect();
42             }
43             if (isConnect == -1) {
44                 return false;
45             }
46             else {
47                 StateNode state = states.get(0);
48                 if (state.getIsKeyState() == 0) {
49                     state.setIsKeyState(origin.isConnect(state));
50                 }
51                 return state.getIsKeyState() != 1;
52             }
53         }
54     }
55 
56 /* StateNode中用来判断是否是关键状态的方法 */
57 public int isConnect(StateNode node) {
58         LinkedList<StateNode> queue = new LinkedList<>();
59         HashSet<StateNode> vis = new HashSet<>();
60         vis.add(this);
61         queue.add(this);
62         StateNode cur;
63         while (!queue.isEmpty()) {
64             cur = queue.removeFirst();
65             if (cur.isFinal()) {
66                 return 1;
67             }
68             for (Map.Entry<TransitionNode,StateNode> entry:
69                     cur.nextNode.entrySet()) {
70                 StateNode next = entry.getValue();
71                 if (next.equals(node)) {
72                     continue;
73                 }
74                 if (!vis.contains(next)) {
75                     vis.add(next);
76                     queue.add(next);
77                 }
78             }
79         }
80         return -1;
81     }

二、四个单元中架构设计思维及OO方法理解的演进

第一单元

第一单元是我真正意义上第一次采用面向对象的思想去解决问题(Pre的作业主要还是熟悉java语法),面对复杂的表达式,面向过程的编程思想显得无从下手。这因为如此,我才更深刻地体会到面向对象分析解决问题的优势,将复杂的表达式层层解析为一个个对象,使用递归下降的方法对其进行解析和处理,最终迎刃而解。但是现在再回头去看第一单元的代码,相比于第四单元还是有很明显的“面向过程”的影子,这也导致部分模块的处理显得尤为臃肿,方法复杂度也非常高。

第二单元

第二单元主要学习多线程编程以及设计模式的应用,这也是我第一次学习使用多线程的方法编写代码,对线程同步互斥的理解还非常浅,导致处理线程同步和线程安全的问题上花费了大量的时间和精力。但同样的,这个单元的作业给我带来的进步也格外显著,不光是熟悉了多线程的使用方法,更重要的是学习到生产者消费者模型和流水线模型,通过在作业中设计这两种模型也加深了对面向对象编程的理解,将调度器、托盘、需求、电梯等设计为对象,分别思考他们的功能,最终作为一个个模块组合为项目,这也让我体会到面向对象编程“分治”的特点。

第三单元

第三单元的核心是学习JML规格化语言,没有太多需要自己设计架构的地方。此外也借此复习了图论的相关算法,并了解到各个基础算法的优化方式。不过这一单元课程组提供的代码架构也确实让我学习到不少经验,这在我第四单元的架构设计中其实也有一定体现。把person、relation分别看做图的节点和无向边,构建起来网络模型,并针对这个网络进行一些功能拓展(如分组,发送信息)。

第四单元

第四单元的架构设计相对于其他单元来说比较简单一些,因为本身UML图就具有非常明显的结构,只要根据各个元素之间的关系层层分析,模仿UML图本身的结构进行设计就好。这个单元主要还是更加熟悉和理解UML图元素,并使用面向对象的思想来解析UML图(其实也非常简单,很容易想到把各个元素作为对象)。

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

四个单元都是与同学合作完成评测机的编写,并一起对拍,受益良多。

第一单元

第一单元是使用同学写的数据生成器随机生成数据,并利用python的sympy库进程正确性验证。尽管没有实际动手去写评测机,但还是深刻的认识到自动评测带来的便利性和优势,同时也从第一单元同学写的评测机中认识到了一些不足,比如没有完全实现自动,出现问题的数据由于过于复杂难以迅速定位错误的地方等,为之后我编写评测机提供了宝贵的经验。

第二单元

第一单元作业认识到使用评测机debug和互测带来的事半功倍的效果,于是从第二单元开始我也编写了评测机。吸取第一单元的教训,我学习到了使用python的os库生成数据后,自动运行jar文件将生成的数据作为输入,得到输出结果后按一定逻辑进行评判。因为第二单元多线程异步性的特点,没办法使用对拍,于是只能自己分析程序运行逻辑来进行评测,这其实存在一定风险,原因是不一定能考虑全所有的情况(第二单元第二次作业也正是这个原因,没有测出作业存在的bug)。另一方面多线程的评测需要高并发地执行程序,否则很难暴露出线程安全、死锁等问题。

第三单元

有了前两次的测试经验,第三单元的评测机写起来还是比较顺利的。但是由于第三单元需要更多考虑算法的时间复杂度,评测机只能作为正确性和功能性的验证工具,压力测试还是需要自己针对算法构造数据。当然除了自动评测之外,第三单元还学习到了一个重要的测试手段——JUnit,但JUnit同样要求测试者要充分考虑各个方法的前置条件等。

第四单元

第四单元的测试机主要是图的构造,指令只需要遍历验证即可。类似于作业的解析方式,数据生成同样是根据UML元素之间的层次关系,自顶向下地进行图的构造,重点在于要控制出现异常的概率,以及如出现关键状态的保证。由于整个过程都是随机生成的,所以对于关键状态的控制还是比较玄学,基本是通过尝试不同参数,找到一个比较容易出现关键状态的参数。

四、课程收获

首先本学期oo课程带来的最大收获就是学习到面向对象的思想,不光是在作业上,包括后面几次的评测机编写,其实都或多或少体会到面向对象的优势。另外也学到很多知识,包括git的使用,单例、生产者消费者等设计模式,markdown的使用,uml图和jml语言等。

另外,一次次作业的迭代开发也让我意识到设计和架构的重要性,一个好的架构能够帮助程序员更加方便和顺利地维护项目、拓展功能,尽量减少各个模块之间的耦合度,防止修改或增加一个功能,会导致其他模块的功能受到影响,最终导致迭代难度过高而重构。

当然还有代码风格的变化,一学期的作业都要求我们按照一定的代码规划进行编写,我也切实感受到经过oo课程的实践,自己代码风格的规范化。比如在os挑战性任务中会习惯性地增加空格,不至于看上去那么紧凑;也会自主地使用规定的方法对变量和方法进行命名,增加代码的可读性。

五、课程改进建议

1.希望能在pre增加一些多线程的知识,第二单元是大多数同学第一次接触设计模式和多线程,难度确实很大,加上对多线程的理解不到位,导致后面的作业也会出现或多或少的问题。

2.希望互测数据不规范时,课程组能够为同学指出具体是哪里不规范,尽管课程组可能是想让同学仔细阅读分析,但很多时候真的很难找到原因。

3.虽然可能不太现实,但希望能适当在第三或第四单元选择两次作业减少一些量,压缩成一次作业,提前一周结束课程设计,让同学有更充分的时间准备考试,烤漆压力确实太大了。

 
posted @ 2022-06-24 01:42  Kazeya_y  阅读(55)  评论(0编辑  收藏  举报