一、UML解析器设计

​ 先看下题目:第四单元实现一个基于JDK 8带有效性检查的UML(Unified Modeling Language)类图,顺序图,状态图分析器 MyUmlInteraction,实际上我们要建立一个有向图模型,UML中的对象(元素)可能与同级元素连接,也可与低级元素相连形成层次关系

输入:上述三UML图的边集(非实体元素)与点集,上下层级元素通过_parent隐式邻接。值得注意的是,不同于前两个单元,我们终于回归了离线算法OFFLINEI/O通过一行END_OF_MODEL分隔,处理难度减小了

输出:针对给出的_name索引到相应的UML元素查询相关对应图中信息,复杂的problemhw15中对循环继承的判断,其他的都能不使用高级算法解决掉

​ 下图是我对本单元作业设计的类图,因为三次作业都是这个架构,下文只按解析器构造流程顺序分析。btw感谢肖同学的排雷帖,可见我对UML的理解不够深入,UMLAttribute可以出现在顺序图中,不应该简单归入类图元素的子类,不过我这样还是能活过本单元的

信息转换——适配器模式

​ 我构建了抽象类MyEle,以及三个子抽象类对应三种UML图中的元素,因为接口给出的结点(对应实体的元素)没有邻接结点的信息,我有必要自定义类来保存这些信息。能将这种“一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的”问题解决的是适配器模式

public class MyAdapt {
//适配器用于转换的类
public static MyEle as(UmlElement e) {
switch (e.getElementType()) {
case UML_CLASS: {
return new MyClass((UmlClass) e);
}
case UML_ASSOCIATION: {
return new MyAssc((UmlAssociation) e);
}...
//
public abstract class MyEle {
//My*类对UmlElement对象有一个依赖,MyEle实现基本信息接口,其他由子类分别考量
private UmlElement ele;
public MyEle(UmlElement e) {
this.ele = e;
}...

​ 在UmlInteraction构造函数中,我首先将传入的UmlElement通过MyAdapt“转换”成My*对象,后者可以从前者中只存储我们关心的信息,一些可以忽略的元素UmlEvent / UmlEndPoint等直接在这里过滤掉

建图

​ 有了这些准备工作开始建图,以类图为例(另两类图如法炮制),通过.mdj解析,边集通过_parent / _reference / UML元素表示,提取后完成建图。我对作业中涉及的所有的UmlElement,包括边集UMLAssociation / UMLGeneralization等都适配自己的类,当然这可有可无,我这样做是为了遍历一遍MyEle对象就完成建图,看起来清晰一点。类图中的顶级元素是类与接口,都管理着方法与属性,StarUML中可以对二者进行很多相同操作,有必要为二者创建父类ClassLike(类实体)来复用代码

​ 根据输入模式,我们在解析器中需要管理按_name索引的Class / StateMachine / Interaction等集合,并且索引查询中要进行有效与重复检查。我为了复用代码采用了泛型容器,分别用Hashmap / Hashset实现有效_name查找表与重复_name

public class MySet<T> {
private HashMap<String, T> na2ele = new HashMap<>();
private HashSet<String> dupName = new HashSet<>();
public void add(T t) {
if (t instanceof MyEle)
if (na2ele.containsKey(t.getName())) dupName.add(t.getName());
else na2ele.put(t.getName(), t);
}
public boolean contains(String s) { return na2ele.containsKey(s); }
public boolean isDup(String s) { return dupName.contains(s); }
...

​ 根据输入,查询请求关心ClassLike自身的层次或直接关联关系ClassLike间接继承/实现的关系,后者如“类的顶级父类 / 实现的全部接口”需要ClassLike继承链上的信息。没有循环继承时继承链为树形结构,由此,我在MyClassLike定义addFather / getFather / update继承相关抽象方法。updateextendsimplementsClassLike进行update后再合并信息,置为更新过的状态,是递归式搜索

​ 输出方面,通过与同学交流可以记忆化搜索(查询时更新),但是这是个OFFLINE问题,同时考虑到题目的数据规模1e2.6,小,在完成建图之后直接对所有类update,全部元素更新后再进行查询也没问题,这样不用每个查询方法都先update一下了

PreCheck

hw15要求进行有效性检查,上文中查询前update显然要放到R002 / R003之间,有效性以_id / _name重复,_visibility等检查为主,很简单;R002检测循环继承,输出继承环路上所有元素,当然不用求出环路,只要求出所有环路上的元素。MyEle处在继承环路上 iff 其处于某阶数大于1的强连通分量上或本身存在自环或一次以自身为源点的搜索经过本身1次以上,故可使用TarjanBFS来检查

二、OOP心路历程

​ 谈到架构设计及OO方法理解的演进,我认为自己本学期确实有长进,起码前两个Unit次次重构,但后两个Unit都是先好好分析设计结构再写的,而且都不很复杂,所以都没有重构

Unit1为层次化设计,hw1 / hw2是纯纯的面向过程,hw3中利用工厂模式根据实际中的实体为表达式中的对象建立了具有继承层次关系的类,通过将求导以及化简操作作为抽象方法,使得因子,表达式啥的都能按照自己的求导法则各自计算导数、化简,输出通过覆写toString(),这是我首次在作业中体会到面向对象中多态的优势。

Unit2为多线程设计,代码结构很简单,采用简单的生产-消费者模式都行,主要还是通过Java库及保留字实现Java线程的同步与互斥,这块一直理不太清又很难调试,所以又重构了好多次,可以说我也是初次接触在线算法问题,电梯调度的优劣是跟数据相关的,其实有点玄学,在这里我初步了解了Java的异常机制,这是保证鲁棒性的方法之一,Java中还有封装好的ReentrantLock可重入锁等锁,我也做了了解

Unit3为规格设计,需要根据JML封装官方包中的接口,首先得说JML表意很准确,不用像指导书般反复研究,这个Unit大家实现的接口功能都是一样,但方法与效果不尽相同,面向对象中就是通过这样的封装实现代码的模块化,调用者很难为封装好的方法优化,作为实现者有义务实现高效的封装。面向过程和面向对象肯定不是对立的,我们实现的代码对外是屏蔽掉的,所以在作业中我在底层函数用了很多面向过程风格的代码

Unit4为模型化设计,我们接触的UML脱离了Java,描述的是对象、属性、状态等,更加接近面向对象的本质,作业方面空间很大,由于hw13就理好了层次,后续轻松拓展,还第一次使用了泛型容器,节约了很多代码量,充分封装各个层次的操作以模块化

三、测试理解与实践

​ 当然我印象最深刻的是多线程程序测试,程序运行结果与环境相关,在测试出现问题后很难通过程序本身找到病因,我使用JProfiler + println()才能逐步确定程序卡在了哪里

​ 测试分两方面,一是验证程序的基本功能,二是对极限数据或异常输入测试,前者通过随机生成样例数据或JUnit单元测试法或对拍实现,后者如电梯换乘调度测试,特殊表达式求导,给定数据规模下复杂度最高的数据。我认为注重后者或许对我们更有价值,反正我的Bug都出在极限情况...还有一种测试就是对优化过的代码进行测试,一定得用修改前版本对拍一下,属实对这一点有ptsd

JUnit对我而言很好用,@Before / @Test / @After完整构建了数据生成、断言测试、错误分析的测试流程,还在同一项目下就能用,舒服啊

四、收获总结

​ 首先我在本学期通过Java认识到面向对象的印象深刻的特征:封装与多态

​ 封装定义为隐藏对象的属性和实现细节,仅对外公开接口,我最开始对每个属性几乎都写了get / set,但这样属性的隐藏原则就没意义了,我理解的封装是没有需求就不开放属性的查询或方法的调用,所以属于对象“份内事”的方法也尽量用private修饰。封装的优势显然,首先封装隐藏了属性,保证了程序的数据安全,封装方法,可以使程序设计更加模块化,比如Unit4中可将三种图的查询分派给三种管理类,这样也能降低类的平均复杂度,还有一点是封装的代码拓展性强,需要改变程序功能时,调用者往往不用改写,只需对封装的对象中的数据结构与方法进行修改,这也是课程迭代开发模式下的要求。当然,既然有这个信息隐藏原则,调用者就很难根据实际问题对封装对象作出调整,导致封装对象已经很难有优化的空间,所以使用封装时,调用者得对对象有一个大体的了解,比如在Unit3中我因为对Hashmap的底层实现不了解,初始容量过大引发了隐患;实现者则需合理利用时空资源保证程序的整体良性

​ 多态,我理解的是同一类型对象引用在运行时可以使用不同的方法以实现“多种形态”,一种形式是子类Override方法后使用父类类型引用,还可以是由不同类实现的接口。通过多态在Unit1实现表达式中不同因子的求导,在Unit4中可以让数据结构不同的Class / Interface(单/多继承)共享一份Tarjan代码,多态巧妙在我们无需去if-else所引用的对象实际是什么类型,调用的方法采用对应继承链上最近的版本,所以多态中想要扩展新子类很简单,只需要符合引用者的行为逻辑,引用新子类的对象则无需修改代码

​ 当然一些别的特征也很酷,我们可以通过继承复用父类的代码,也可以通过泛型类来复用代码,并且实现类似多态的效果,不过泛型类在构造传入类型时,其所管理的类型就确定了,所以我觉得泛型倒有一点c/c++#define的意思


​ 不难看出,上面这些特征,无论是类也好,接口也好,其共性是对对象行为范式的一种抽象,OO语言不同于PO语言的一点就是它给了一套这种抽象的机制,所以面向对象编程更贴近实际,我们代码的结构往往对应实际中的逻辑关系,可能这也是Java的优势,虽然不精炼,但很直观易懂

​ 在理论网课 / 实验课中,我也学到了OO设计模式与原则,OO的可维护性强,很适合于迭代开发与复杂系统的整体设计,学习OO编程思想,更深入地理解Python / C++等的OO机制了

五、我的改进意见

  • 看到这么多同学吐槽实验课我就放心了,我认为实验课有必要给同学们一定的反馈,具体的形式可以是编程性实验之后可以在平台上像作业一样了解测试情况并且进行Bug修复

  • 感觉后两Unit对可维护性要求降低了,Unit1中嵌套的出现打破了原本简单的表达式到因子的层次关系,Unit2中多部电梯的出现改变了控制类的数据结构,也改变了线程同步的条件,而Unit3 / 4在迭代开发中基本是跟随指导书在上一次作业中多写些方法增加功能就okay了,反正就是对原先的作业架构的冲击力不够大,应该提高难度,btw程序高效性也很重要,希望后两个Unit能开启性能分

  • Unit3可以放到前面,体现出类规格,层次间的规格

  • 互测本意应该是驱动大家学习其他同学代码中的设计策略吧,建议发动Hack的时候得提交自己发现的Bug的位置,防止互测变成纯黑箱互测

六、网课学习体会

​ 条件艰苦的线上网课,颇怀念线下的课堂氛围和peer pressure呢

​ 感谢仍离同学们最近的老师与助教们~

BUAA_OO_第四单元的更多相关文章

  1. linux基础-第十四单元 Linux网络原理及基础设置

    第十四单元 Linux网络原理及基础设置 三种网卡模式图 使用ifconfig命令来维护网络 ifconfig命令的功能 ifconfig命令的用法举例 使用ifup和ifdown命令启动和停止网卡 ...

  2. 北航oo作业第四单元小结

    1.总结本单元两次作业的架构设计 在我动手开始总结我的设计之前,我看了其他同学已经提交在班级群里的博客,不禁汗颜,我是真的偷懒.其他同学大多使用了新建一个类,用以储存每一个UMLelemet元素的具体 ...

  3. OO 第四单元总结

    一.总结本单元两次作业的框架设计 1.1. 需求分析 通过分析mdj文件可知,两次作业如果对于时间复杂度没有要求,可以不涉及任何数据结构,直接根据读入的UML_ELEMENT逐个分析得到各个函数的结果 ...

  4. oo作业第四单元总结暨结课总结

    目录 一.第四单元作业架构设计 1.第一次UML作业架构设计 2.第二次UML作业架构设计 二.架构设计和OO方法理解演进 三.测试理解与实践的演进 四.课程收获总结 五.三个具体改进建议 一.第四单 ...

  5. OO第四单元总结

    单元架构设计 本单元OO作业主要涉及两个过程,即先根据输入的elements数组建立UML存储模型,而后基于这个模型实现一系列查询判断功能.汲取上单元的经验,建模过程中模型数据容器的选择依据要求实现的 ...

  6. 面向对象程序设计第四单元总结(UML系列)

    2019面向对象程序设计第四单元总结 前言 ​ 本单元是面向对象程序设计课程的最后一个单元了,本单元是和UML模型相关,也就是说,我们需要正确理解UML模型的基础上,对构建出的UML模型进行解析,但是 ...

  7. OO第四单元博客

    第四单元博客 这个单元的作业,emmmm助教们做的工作还是一如既往的多,我们只负责添一添代码,最后一次作业了,感谢各位助教和老师,同时也希望我能顺利通过这最后一关. 架构设计 第一次作业架构展示 第一 ...

  8. OO第四单元总结——查询UML类图 暨 OO课程总结

    一.本单元两次作业的架构设计总结 作业一.UML类图查询 1. 统计信息图 2. 复杂度分析 基本复杂度(Essential Complexity (ev(G)).模块设计复杂度(Module Des ...

  9. 2019年北航OO第四单元(UML任务)及学期总结

    第四单元两次作业总结 第十三次作业 需求分析 本次作业需要完成一个UML类图解析器,所需要解析的只有符合UML标准和能够在Java 8中复现的UML类图.查询指令存在两种:仅与所查对象有关的指令,以及 ...

随机推荐

  1. es命令测试

    1.新建索引并赋值 :put/索引名/文档名/id //文档名后面会逐渐取消 相当表 PUT /test1/type1/1{ "nmae":"hb", &quo ...

  2. scala:分别使用懒汉式和饿汉式实现单例模式

    在java中,单例模式需要满足以下要求: 构造方法私有化,使得本类之外的地方不能使用构造方法new出对象 提供私有静态属性,接收单例对象 公共的.静态的getInstance方法,便于外界拿到单例对象 ...

  3. 微信小程序:picker组件实现下拉框效果

    一.wxml中代码 <view class="in_order_Param">             <text>状态:</text>     ...

  4. Python处理不平衡数据

    参考文献 所谓的不平衡数据集指的是数据集各个类别的样本量极不均衡.以二分类问题为例,假设正类的样本数量远大于负类的样本数量,通常情况下通常情况下把多数类样本的比例接近100:1这种情况下的数据称为不平 ...

  5. Linux关机指令详解

    Linux关机指令 在linux领域内大多用在服务器上,很少遇到关机的操作.毕竟服务器上跑一个服务是永无止境的,除非特殊情况下,不得已才会关机. 正确的关机流程为:sync > shutdown ...

  6. deepin 340 USB转console线驱动安装及使用

    刚换DEEPIN系统, 有个路由器要做调整,的确是没windows友好,查了网上资料,归总一下. zhaodong@zhaodong-PC:sudo find / -name serial 进入 zh ...

  7. 低功耗蓝牙 ATT/GATT/Service/Characteristic 规格解读

    什么是蓝牙service和characteristic?如何理解蓝牙profile? ATT和GATT两者如何区分?什么是attribute? attribute和characteristic的区别是 ...

  8. 记录vue springboot 跨域采坑

    vue配置 域名src\main.js要与config\index.js一样 var axios = require('axios')axios.defaults.baseURL = 'http:// ...

  9. CentOS7系统重置root密码

    https://blog.csdn.net/qq_42969074/article/details/88080821

  10. 清除浏览器默认样式的reset.css(转载于reset.css的官方)

    /* http://meyerweb.com/eric/tools/css/reset/ v2.0-modified | 20110126 License: none (public domain) ...