BUAA-OO-第四单元总结

打印 上一主题 下一主题

主题 692|帖子 692|积分 2076

BUAA-OO-第四单元总结

一、第四单元架构设计

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


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



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


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

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

  • 解析class, interface, interaction, stateMachine, collaboration
  • 解析operation, associationEnd, generalization, realization, attribute, lifeline, endpoint, region
  • 解析parameter, message, state, association
  • 解析transition
  • 解析event
值得一提的是,尽管从层次上来看,association的层级应该高于associationEnd。但由于本次作业中关注的不是和类同一端的端点,而是另一端的端点,因此笔者采用了先解析associationEnd,再解析association的方法。
指令执行过程

由前文可知本单元架构设计具有明显的层次性,这样的好处是在执行指令或检查合法性时,其方法的调用过程也有极强的层次感,这样每一层只用处理这一层该处理的事,剩下的交给下一层即可,这样的结果是逻辑清晰,容易定位错误。这里以状态图的指令2(给定状态机模型和其中的一个状态,判断其是否是关键状态)为例,简单介绍调用过程。
  1. 1 /* MyImplementation 直接调用StateGraph的方法 */
  2. 2 public boolean getStateIsCriticalPoint(String stateMachineName, String stateName)
  3. 3             throws StateMachineNotFoundException, StateMachineDuplicatedException,
  4. 4             StateNotFoundException, StateDuplicatedException {
  5. 5         return stateGraph.getStateIsCriticalPoint(stateMachineName, stateName);
  6. 6     }
  7. 7
  8. 8 /* StateGraph 简单判断异常后调用StateMachineNode的方法 */
  9. 9 public boolean getStateIsCriticalPoint(String stateMachineName, String stateName)
  10. 10             throws StateMachineNotFoundException, StateMachineDuplicatedException,
  11. 11             StateNotFoundException, StateDuplicatedException {
  12. 12    ArrayList<StateMachineNode> stateMachines = name2StateMachine.get(stateMachineName);
  13. 13         if (stateMachines == null) {
  14. 14             throw new StateMachineNotFoundException(stateMachineName);
  15. 15         }
  16. 16         else if (stateMachines.size() > 1) {
  17. 17             throw new StateMachineDuplicatedException(stateMachineName);
  18. 18         } else {
  19. 19             return stateMachines.get(0).getStateIsCriticalPoint(stateName);
  20. 20         }
  21. 21     }
  22. 22
  23. 23 /* StateMachineNode 直接调用RegionNode的方法 */
  24. 24 public boolean getStateIsCriticalPoint(String stateName)
  25. 25             throws StateNotFoundException, StateDuplicatedException {
  26. 26         return region.getStateIsCriticalPoint(stateName);
  27. 27     }
  28. 28
  29. 29 /* RegionNode 判断异常后,调用StateNode的方法 */
  30. 30 public boolean getStateIsCriticalPoint(String stateName)
  31. 31             throws StateNotFoundException, StateDuplicatedException {
  32. 32         ArrayList<StateNode> states = name2StateNode.get(stateName);
  33. 33         if (states == null) {
  34. 34             throw new StateNotFoundException(machineName, stateName);
  35. 35         }
  36. 36         else if (states.size() > 1) {
  37. 37             throw new StateDuplicatedException(machineName, stateName);
  38. 38         }
  39. 39         else {
  40. 40             if (isConnect == 0) {
  41. 41                 isConnect = origin.isConnect();
  42. 42             }
  43. 43             if (isConnect == -1) {
  44. 44                 return false;
  45. 45             }
  46. 46             else {
  47. 47                 StateNode state = states.get(0);
  48. 48                 if (state.getIsKeyState() == 0) {
  49. 49                     state.setIsKeyState(origin.isConnect(state));
  50. 50                 }
  51. 51                 return state.getIsKeyState() != 1;
  52. 52             }
  53. 53         }
  54. 54     }
  55. 55
  56. 56 /* StateNode中用来判断是否是关键状态的方法 */
  57. 57 public int isConnect(StateNode node) {
  58. 58         LinkedList<StateNode> queue = new LinkedList<>();
  59. 59         HashSet<StateNode> vis = new HashSet<>();
  60. 60         vis.add(this);
  61. 61         queue.add(this);
  62. 62         StateNode cur;
  63. 63         while (!queue.isEmpty()) {
  64. 64             cur = queue.removeFirst();
  65. 65             if (cur.isFinal()) {
  66. 66                 return 1;
  67. 67             }
  68. 68             for (Map.Entry<TransitionNode,StateNode> entry:
  69. 69                     cur.nextNode.entrySet()) {
  70. 70                 StateNode next = entry.getValue();
  71. 71                 if (next.equals(node)) {
  72. 72                     continue;
  73. 73                 }
  74. 74                 if (!vis.contains(next)) {
  75. 75                     vis.add(next);
  76. 76                     queue.add(next);
  77. 77                 }
  78. 78             }
  79. 79         }
  80. 80         return -1;
  81. 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.虽然可能不太现实,但希望能适当在第三或第四单元选择两次作业减少一些量,压缩成一次作业,提前一周结束课程设计,让同学有更充分的时间准备考试,烤漆压力确实太大了。
 
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表