OO第四单元总结&课程总结

OO第四单元总结&课程总结


 

I. 第四单元总结

一、架构设计

1、UML 1

(1) 需求概述

  第一次UML作业要求完成一个UML类图的分析器。通过读入经过预处理的mdj文件(将其中的类图(UmlModel)信息筛选后提取出来),将其有序存储并根据存入内容对查询指令进行响应。

(2) 流程分析

  从流程上可以分成两个部分:构造数据结构,响应查询请求。出于性能考虑(被之前强测卡cpu time吓怕了),我在构造数据结构的时候增设了大量工作量以保证绝大多数查询请求能够保持O(1)的复杂度。

  构造数据结构部分,出于功能的考虑,我将每个Uml类进行了再封装(UmlClass->MyClass)。每个封装的类里面除了原有的类对象(UmlElement)还有为了方便查询而设置的数据结构(比如为了通过id快速检索而设置的HashMap<String,MyClass>等),有些查询结果同样直接放置在类里面(比如为每个UmlClass中违反数据安全性的UmlAttribute设置了一个BadList来直接存放)。为了实现这个功能我的构造函数分成了两个阶段

  阶段一:读入所有Element并进行分类

  阶段二:将每个Element进行组合分配

  阶段三:利用已经被分类的Element构造数据结构

  以下是三个阶段的代表函数

 

  Stage1:

    private void distribute(UmlElement element) {
        ElementType type = element.getElementType();
        if (type.equals(ElementType.UML_CLASS)) {
            MyClass.addClass(element);
        } else if (type.equals(ElementType.UML_ATTRIBUTE)) {
            MyAttribute.addAtt((UmlAttribute) element);
        } else if (type.equals(ElementType.UML_OPERATION)) {
            MyOperation.addOpe((UmlOperation) element);
        } else if (type.equals(ElementType.UML_PARAMETER)) {
            MyOperation.addPar((UmlParameter) element);
        } else if (type.equals(ElementType.UML_INTERFACE)) {
            MyClass.addClass(element);
        } else if (type.equals(ElementType.UML_INTERFACE_REALIZATION)) {
            MyInterface.addInterReal((UmlInterfaceRealization) element);
        } else if (type.equals(ElementType.UML_GENERALIZATION)) {
            MyGeneralization.addGen((UmlGeneralization) element);
        } else if (type.equals(ElementType.UML_ASSOCIATION)) {
            MyAssociation.addAss((UmlAssociation) element);
        } else if (type.equals(ElementType.UML_ASSOCIATION_END)) {
            MyAssociation.addAssEnd((UmlAssociationEnd) element);
        }
    }

 

  这个方法功能十分明显,就是将Element根据其type分门别类的放置到不同的自定义类中。

 

  Stage2:

    private static ArrayList<UmlOperation> opeList = new ArrayList<>();
    private static HashMap<String, UmlOperation> idMap = new HashMap<>();
    private static HashMap<UmlOperation, Integer> typeMap = new HashMap<>();

    public static void addOpe(UmlOperation uo) {
        opeList.add(uo);
        idMap.put(uo.getId(), uo);
        typeMap.put(uo, 0);
    }

  为了避免把整个代码全搬上来,在这里只是举几个比较有代表性的例子。这段代码的功能就是把每一个添加的方法(UmlOperation)存储到三个数据结构中。可以看到,这三个数据结构并非都是直接面向最终的查询指令的。这些结构更多是服务于接下来的阶段。

 

  Stage3

    private static void parTrim() {
        String opeId;
        UmlOperation uo;
        int type;
        for (UmlParameter up : parList) {
            opeId = up.getParentId();
            uo = idMap.get(opeId);
            if (up.getDirection().equals(Direction.RETURN)) {
                type = 2;
            } else {
                type = 1;
            }
            int origin = typeMap.get(uo);
            typeMap.put(uo, origin | type);
        }
    }

    public static void trim() {
        parTrim();

        String classId;
        MyClass mc;
        int type;
        for (UmlOperation uo : opeList) {
            classId = uo.getParentId();
            mc = MyClass.id2Class(classId);
            type = typeMap.get(uo);
            mc.addOpe(uo, type);
        }
    }

  这两个方法的功能可能并不直观。它的含义就是将之前组织存储好的数据结构进行组合搭建。之前两个阶段的功能都是线性的,只是将每个元素分到了不同的组中;而这个阶段的功能就是将元素和元素之前的关系搭建起来。比如这部分代码的功能就是先将Operation的Parameter放置到对应的Operation中,再将Operation通过parentId的索引放置到对应的类中。当然在类中还会对传入的Operation进行进一步的整合,在这里就不在赘述实现细节了。

  查询响应部分,没什么好说的,基本上就是读取某个数组的长度或者输出某个Set的全部内容。就像之前所说,O(1)复杂度,你值得拥有

  一个简单的例子。在这个查询请求响应方法中,我只需要找到目标类并获取一个数组内容即可。

(3)评价

  总体而言这次作业是我整个OO课程完成的自认为最满意的代码。我在动手写之前将每一个查询请求进行分析整合,并依据请求进行数据结构以及处理流程的设计。正因如此,我的代码从结构上十分清晰,不同功能之间耦合度低,保证了整个项目的稳定性与可维护性(出了bug能够快速定位排查,并且只需较少的代码量即可解决问题)。

  为了保证性能,我的大多数任务在数据结构构造阶段就已经完成了。比如为了查询类的所有实现接口,如果考虑继承的话需要将其实现接口的所有父类接口全部遍历。但采取的是在构造阶段对全部类进行图遍历,对于每个继承关系从上至下如同滚雪球一般完成了全部数据的更新。从本质上是利用一个O(n)复杂度的构造操作代替多个O(n)复杂度的查询操作。

2、UML 2

 

(1) 需求概述

 

  第二次UML作业要求完成一个包含类图、顺序图以及状态图的分析器。同样通过读入经过预处理的mdj文件,将其内容有序存储并根据存入内容对查询指令进行响应。与此同时,还需要提供自检功能,按照三个不同规则对传入的数据进行检验。

 

(2) 架构分析

 

  这次的功能主要分成四个部分:类图查询、顺序图查询、状态图查询、规则检查。其中我第一个部分照搬了上次的代码,第二三部分相似度较高因此只说明状态图部分,第四个部分是较为不同的因此将详细说明。

  状态图查询。相比较第一次对类图的查询,状态图的查询难度无疑低了许多。与上次作业相同,我对状态图的处理同样分成三个阶段。先将元素分类存储,在进行预处理建立数据结构,最后将相关元素相互联系形成结构以待查询。状态图的前两个功能均是计数查询,只需要输出数组大小即可;第三个功能需要查询给定状态的所有后继状态,为了提前准备好只需要反向遍历即可。

  规则检查。这部分内容与之前的功能差别较大。接下来会对每个规则的实现进行分别讲解。

  R002:重名属性与关联对端

    通过建立一个nameSet的HashSet对于每一个新加入的attribute和associationEnd进行统计记录,如果有重名的加入到dupSet中,最后输出即可。

  R008:循环继承

    由于在一个成环的继承链中只可能出现接口或是类其中一种(类实现接口但接口无法实现类),因此只需要考虑继承关系即可。对每一个类/接口进行图遍历,记录到达每一个点的路径集(到达这个点路径中访问的所有元素),如果遍历过程中访问了自身那么将这个点对应路径集中全部元素加入到结果集当中。如果到同一个点有多条路径则合并这些路径集。对于每一个找到的路径集中的元素,由于其所在的所有环均已被找到,因此将其从遍历范围中移除。

  R009:重复继承

    与R008类似,同样是对图进行遍历访问。如果找到到达同一个点的第二条路径那么将这个类/接口以及其所有子类/接口加入到结果集当中。不同于R008的是R009需要考虑类对于接口的继承。同时,R009只有在R008check通过之后才能进行,因为如果出现成环情况可能会在遍历全部子类/接口时出现死循环。

二、Bug分析

1、UML 1

(1)NullPointerException

  在获取方法Visibility时忘记考虑查询失败导致的空指针情况,因此一旦查询的方法不存在就会触发NullPointerException.

  这个问题的原因本质在于测试覆盖度较差。由于懒得用之前单元的junit等测试工具导致没有检查出这个问题。

2、UML 2

(1)NullPointerException

  哎,说什么好。

三、优化思路

1、Floyd思路

  在找最短路径的算法中Floyd旨在通过遍历查找出任意两点的最短路径。这种算法的优势在于一旦完成一开始的O(n^3)复杂度的遍历,之后的查询复杂度可以实现O(1)。尽管Floyd作为一个最短路径算法并不算优,但是这种思路值得借鉴。在本单元的作业中我充分利用了这种思路,将许多需要查询的内容通过一次复杂度较低的遍历提前预置,这样在查询的时候可以最大程度的节省时间。

  才怪

  这样的思路在这次作业中并没有节省时间,从根本上讲还是由于查询指令的条数较少,即使是每次查询时小规模遍历所需要的时间也比开始时大规模遍历时间来得要少。因此这种思路的实现有着严格的约束条件

  1)查询指令数目较多,尤其是复杂度大的查询指令

  2)不会在查询中对数据进行增删改。

II. 课程总结

一、内容梳理

1、Section 1

  本单元解决的主要问题是多项式的求导。以下是三次作业的具体内容:

  1)第一次:a*x^b求导

  2)第二次:a*x^b*cos(x)^c*sin(x)*d

  3)第三次:表达式求导

  实现要素:

  1)第一次:简单类的封装,正则表达式匹配

  2)第二次:简单类的封装,正则表达式匹配,边界情况考量

  3)第三次:复杂类的封装,类之间的迁移,复杂架构设计,边界情况考量

  这个单元前后的连贯性较弱,比较难以复用之前作业的代码(除非你第一次作业就把第三次作业的功能实现)。总体而言这个单元旨在增强对java这门语言特性的熟悉,尤其是面向对象能力的考核强度逐级增加。第三次作业尤其如此,需要对表达式进行分类封装,并且不同类表达式和表达式之间的转移也是考量的重要要素。

2、Section 2

  本单元解决的主要问题是多线程。以下是三次作业的具体内容:

  1)第一次:单部电梯无捎带

  2)第二次:单部电梯有捎带

  3)第三次:多部电梯有转乘

  实现要素:

  1)第一次:单线程调度,简单锁

  2)第二次:单线程调度,简单锁

  3)第三次:多线程调度,复杂锁,生产者消费者模型,优化设计

  这个单元连贯性较强,一个合理的电梯类设计可以贯穿始终。多线程的思想渗透在每次作业中,尤其是第三次作业,需要合理的规划每一个电梯线程之间的调度合作,以及对于请求的发送/接受管理,都需要合理的多线程设计。

3、Section 3

  这单元的主要内容是如何暴力优化算法以pass强测性能线jml规范和规格化的学习。jml是规格化语言的简称,这个单元的作业主要是利用给出的jml编写符合要求的代码。从jml角度这三次作业没有太大的区分度,从算法层面则有较大的不同。

  1)第一次:动态增减数组数据库

  2)第二次:利用指令交互查找最短路径

  3)第三次:利用指令交互解决地铁换乘问题

  不知为何,这单元虽然标明了是jml学习,但大部分的时间和精力都在对算法的实现以及修改上。第三次作业性能要求和功能复杂程度都较高,反倒是jml的语法与之前两次作业没有太大的区别。

4、Section 4

  见第一部分

二、心得理解

1、架构设计

  1)继承思想

  继承不应当仅仅基于认知逻辑更应当基于功能。比如第一单元第三次作业的求导。我将多项式分成三个完全不同的类:元素,项(由多个元素构成),表达式(由多个项构成)。为了处理各种情况我不得不将它们之间的转换用十分复杂的方式设计。然而事实上求导这个操作仅仅应当在表达式层面出现,所谓的元素和项应当是表达式中的组成部分。因此元素和项应当作为表达式的子类出现,它们应当具备表达式的方法和属性,而不是作为独立于表达式的类存在。如此一来在求导处理的时候这三者之间的转换会更为简洁明了,而不需要单独设立复杂的转换逻辑。

  2)封装思想

  java作为一个面向对象的语言,能否充分利用其封装性是保证代码安全性和降低耦合度的关键。尽管我的程序从一开始就考虑到封装,但其实现效果却不尽人意。

  在第11次作业中,我们需要完成一个可以计算加权路径的最短路径算法。在构造类的时候我将三种不同的加权方式放在了同一个类中,用三种不同的方法实现。这样一来就出现了大量的重复代码,同时这几个方法与所在类有着较高的耦合度,极大地影响了代码的可维护性。

  在第14次作业中我针对每个不同的UmlElement建立了独立的类,每个类之间的沟通利用少量的方法实现。这样一来每个功能便可以在单独的类中独立地实现,体现了良好的封装性。

2、测试与实践

  1)编程习惯

  我最早的编程习惯是这样的:拿到指导书,从看到的第一行开始码出对应的代码,然后随着阅读的推进开始出现这样的循环:码新代码->调整旧代码->建立统一架构整合两部分代码->码新代码。可以看到,这种习惯导致对于一些规模稍大的项目,我会在不断统一新旧代码的工作上花费越来越多的时间,并且更多的任务量往往导致更多的bug。

  因此,我摒弃了这种思路,而是逐渐学习从顶层向下建立整体代码框架。先将任务需求逐一分析整合,建立模块解决不同任务所需要的功能,接着从上层创建框架将这些功能统一整合起来,完成整个代码的部署。

  新的习惯带给我完全不同的编程体验。其一,代码的可维护性提升,由于每一部分代码在整个架构中的位置从一开始就已经决定,因此当出了问题可以快速排查到问题相关的类或是方法,极大节省了debug的任务量;其二,功能的编写速度提升。之前的功能编写往往会遇到一个范围的问题,不清楚当前这个功能哪些应在做到,哪些应该留给别的方法完成。现在自顶向下的架构解决了这个问题,在实现具体功能之前我就已经设计好这个功能的能力边界了。

  

  这是我为UML第一次作业写的需求分析

  2)测试方法

  一开始手头上唯一的测试方法就是自制评测机大量数据暴力测试。

  接着在JML单元中接触了junit等模式测试手段。尽管操作上存在难度,并且某些特殊情况(基本上源于代码的高耦合)不便于测试,但这种测试方法仍然不失为一种更合理更有效的测试手段。

三、收获

  首先面向对象编程这门课程是我上大学(上6系)一来接触的最接近心目中21系的课(什么鬼)。这门课极大地提升了我的编程能力,与此同时让我从不同角度掌握了一门面向对象编程语言的种种特性。以下是具体的收获

  编程能力

  每周一次的项目实践以及隔周一次的实验课都保证了有足够的代码量用以训练;卡性能的强测以及不公布数据的中弱测保证了编程时的小心谨慎;互测时的暴力测试以及打击报复保证了不会再轻信他人。总而言之,从编程能力上这门课给我带来的收获是巨大的。尽管美中不足的是没有完整项目的训练(包含UI,团队合作等元素),然而这种专项训练反而更加保证了我对于具体功能实现以及项目架构上的得心应手。在学期末的时候我短暂地参与了一个软工哥们的项目,惊叹于他们代码的高度组织性以及格式,封装度的混乱有序,我真心为自己身在六系而感到庆幸。

  组织架构能力

  这门课程的训练纠正了我大量之前编写小程序中遗留下来的糟糕习惯。从不读需求莽代码到分析需求列架构,回头看来真是字字血泪。尽管在这方面我扔有不足,但面向对象这门课让我认识到的编程思想会一直保留下去,并在我未来的实践中愈磨愈利。

  抗压能力

  不说了,大家都懂的

四、建议:)

1、指导书措辞

  建议指导书对于一些关键问题的措辞多加打磨,并及时把讨论结果更新至指导书中,避免同学因为这个原因浪费时间构造错误的代码。

2、减少对强测性能的考量

  许多单元的测试偏离了本意。比如jml单元对于jml语言的掌握,在算法层面有了太多涉及并且在强测中利用性能作为考度依据之一。既然如此希望要么加入成系统的算法讲解,至少给出自学的方向以及需要达到的效果;要么减少这一部分的占比,挂羊头卖狗肉可能只能便宜那些爱吃狗肉的人。

3、加入合作成分

  这个建议可谓是冒OO课程之大不讳,一个鼓励丛林法则摒弃相互信任的课程居然建议加入合作成分?我想未来的绝大多数项目都是由多人合作完成的,java这门语言的很多设计也是为了多人之间能够良好协作而设的。因此为什么不加入一些合作项目呢?即使是作为额外加分也可以,总之应该对此有所涉及。

 

 

转载于:https://www.cnblogs.com/socrates1232/p/11068032.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值