OO Unit4 UML(United Modeling Language)
将从第四单元架构设计、四个单元中设计思维变化、测试理解与实践、课程总结收获、改进建议展开
第四单元架构设计
本单元我没有使用诸多类将相关的数据进行封装,而只是在整体上将属于不同UML图中的元素下放到ClassModel、SequenceDiagram、StateChart三个类中,在每个类中管理该类对应的前束检验、查询方法。类的设计应当是以复杂度为标准的,数据结构的复杂度在能掌控的范围之内则不需要过度抽象。
具体而言,对于parent有明确语义的元素,将他们归属到parentId对应的元素集中,实现诸如查询一个Class对应的xxx的功能。
//ClassModel private final HashMap<String, HashSet<UmlElement>> attributes; private final HashMap<String, HashSet<UmlGeneralization>> generalizations; private final HashMap<String, HashSet<UmlElement>> interfaceRealizations; private final HashMap<String, HashSet<UmlElement>> operations; private final HashMap<String, HashSet<UmlElement>> parameters; //StateChart private final HashMap<String, HashSet<UmlElement>> outTransitions; private final HashMap<Pair<String, String>, HashSet<UmlElement>> transitions; private final HashMap<String, HashSet<String>> graph; private final HashMap<String, HashSet<UmlElement>> events; private final HashMap<String, String> begin; private final HashMap<String, HashSet<UmlElement>> ends; private final HashMap<String, Integer> stateCnt;
对于诸如这样HashMap<String, HashSet<>>的结构要警惕空指针的问题,比较好的方法应该是设计一个容器实现对这个的增删改查,可惜意识到这一点时已经写得差不多了,没有再修改。
对于检验参数合理性的方式,我采用以下方式,看起来还算优雅(
private static final Map<Direction, HashSet<String>> VALID_PARA = Collections.unmodifiableMap(new HashMap<Direction, HashSet<String>>() { { put(IN, Stream.of("byte", "short", "int", "long", "float", "double", "char", "boolean", "String") .collect(Collectors.toCollection(HashSet::new))); put(RETURN, Stream.of("byte", "short", "int", "long", "float", "double", "char", "boolean", "String", "void") .collect(Collectors.toCollection(HashSet::new))); } });
而对于按名存取、比较是否重名等功能,类似地设置name对应到Set的映射即可
其中查询获取Class、Lifeline时抛出的异常都是相似的,应当抽象成方法完成。
private String getStateId(String smName, String stateName) throws StateMachineNotFoundException, StateMachineDuplicatedException, StateNotFoundException, StateDuplicatedException { //do something } private String getClassId(String className) throws ClassNotFoundException, ClassDuplicatedException { //do something }
第二次作业要求判定关键状态,可以遍历两次,若允许访问当前节点可以到达任一终点,不访问当前节点无法到达任一终点则说明是关键状态。(然而玩嗨皮了忘提交最后一次修改了呜呜呜)
第三次作业增加前束参数检查,值得注意到的是以下几点:
-
Attribute的归属问题
我将Attribute放到最后再传入(遇到Attribute则传入则无法分辨类图和顺序图中的Attribute,只能依托检查时判定是否parentId出现在当前图中,不够优雅)
-
循环继承判定
采用了tarjan找元素个数>=2的强连通分量
-
重复继承判定
dfs+记忆化搜索,维护访问过的节点。
其中为了压缩类图的行数,我将tarjan求强连通分量的部分外置,并一贯地用了不少stream
设计思维变化与理解演进
第一单元 表达式解析
实现的功能其实是一些数学运算,根据优化程度不同可能运算能力分布于初中高中之间(
这一单元对设计思维要求较高,需要在理解递归下降法的基础上完成对“类”这一抽象层次结构的功能分划。这个过程中我的类的功能完全服务于计算和优化。
此时我对类的理解更多是封装数据和操作、提供数据间的接口的层面。同时本单元聚焦于层级化设计,我也对高内聚低耦合的思想产生了共鸣。
第二单元 电梯调度
这一单元更多希望我们熟悉多线程与线程安全的保证。设计上采用生产者消费者模型,线程安全上主要采用了管程synchronized和信号量Semaphore来实现。算法上采取了look算法,并进行了量子电梯、重构估价函数等优化。
过程中我学习到了策略与机制分离的思想。同时对于多线程共享资源、临界区的设计有了一定的认识。
第三单元 社交网络
本单元强调规范与束缚,可谓是“戴着镣铐跳舞”,设计上主要层次化的类已经给出,架构都大同小异,只需理解JML描述并实现相应的功能即可。
过程中我对约束、契约式编程等概念有了新的认识。
第四单元 UML解析
本单元实现的是指令查询,从做的事的角度来说和第三单元没有显著差别。
对类的职能划分的理解更多是划分复杂度。
对测试的理解与实践
第一单元测试需要覆盖到边界情况,过程中发现的典型问题源自深拷贝与浅拷贝、优化对于边界情况判定上。这一单元的测试我采用手动构造边界数据完成,能够较好的看到优化带来的典型场景数据长度缩减,缺点在于手动构造边界覆盖性难以保证。
第二单元测试需要针对线程安全完成测试,本单元互测出现的问题也有一定的典型性:第一次没有获取讨论区提供的信息,第二三次均是优化的边界出问题。对于优化应当持谨慎态度。测试采用和同学对拍与手动编造极端数据实现,仍然是手动编造的数据有一定的杀伤力也有一定的局限性。
第三单元测试需要顾及性能,在堆优化与其他算法优化的前提下的正确性,测试方案同上,在手动构造初筛之后通过对拍进行测试。同时面向讨论区问题构造了一组数据(
第四单元测试前两次作业具有普遍性,用到了uu的数据生成测试,可惜测出问题改了忘交了。第三次作业需要手动构造,同样面向讨论区构造了一组数据。
然而在实际生产实践中并没有现成的“讨论区”供我们扩展自己测试数据的边界,这就要求我们写的过程中对诸个可能出现的问题多审视多白盒测试,完成一部分功能测试一部分、及时和相关人员(小伙伴、产品经理)消除自然语言二义性等。
课程收获
-
基本掌握了java语法、特性、自学方案
-
学习了面向对象思想与功能拆分解耦合的一些方案
-
了解到了一些常用的设计模式
-
初步了解了多线程的问题与解决方案
-
掌握了一些图论算法
-
养成了较好的代码风格(Command A + Command option L。。)
课程建议
-
设置提问奖惩制度以提高提问质量,比如指导书中指明的信息提问记录一次不良记录
-
预习内容可以再提前两天下放
-