OO第四单元总结

2019面向对象课程序设计第四单元总结

一、第四单元作业框架

这一单元的作业是基于UML图实现的,在前几次的单元总结中,我们就“画过”UML图。在本单元中,我们学习了UML的类图、顺序图以及状态图,分析mdj文件。在正确理解UML模型的基础上,对构建出的UML模型进行解析。

本单元作业的难点不在算法上,而且,即使在强测中也没有卡时间的数据,所以不需要在复杂度上做过多的优化。本次作业真正困难的点是理解UML图以及注意一些细节

第一次作业分析

类图

 

复杂度:

 第一次作业我的思路是:建立一个ElementContainer类,把每一个UMLCLASS以及属于这个CLASS的Attribute和Operation都用放如同一个容器中,接口同理。这样操作会分为两类:一是只涉及一个类的操作,可以直接计算出,并返回到MyUmlInteraction类中;二是设计多个类的操作,需要在MyUmlInteraction中将多个类联系起来,类之间的关系有继承、实现、关联等。至于UmlElement的存储方式,由于每一个UmlElement的Id是不同的,所以以id作为Key建立HashMap。同时,对于有父子关系的UmlElement用另一个HashMap存储关系。

MyUmlInteraction中的UmlElement存储

    private HashMap<String, ElementContainer>
            container = new HashMap<>();
    private HashMap<String, ElementContainer>
            interfaceContainer = new HashMap<>();
    private HashMap<String, Integer> numMap = new HashMap<>();
    private HashMap<String, String> nameToId = new HashMap<>();
    private HashMap<String, String> idLink = new HashMap<>();
    private HashMap<String, ArrayList<String>> genera = new HashMap<>();
    private HashMap<String, ArrayList<String>> association = new HashMap<>();
    private HashMap<String, ArrayList<String>> associationEnd = new HashMap<>();

ElementContainer中的UmlElment存储

    private HashMap<String, ArrayList<String>> nameToId;
    private HashMap<String, UmlElement> attribute;
    private ArrayList<UmlElement> idAttribute;
    private HashMap<String, Integer> numOfAttribute;
    private HashMap<String, UmlElement> operation;
    private HashMap<String, ArrayList<UmlElement>> parameter;
    private ArrayList<UmlElement> interfaceRealization;
    private UmlElement umlElement;

第二次作业

类图

 复杂度

这一次作业与第一次相比增加了顺序图和状态图以及有效性检查。同样,代码实现的难度不大,关键在于理解和避开坑点。但幸运的是,我们在讨论中提到的坑点大多没有出现测试数据中。但需要注意的是本次的输入不止是一个图的内容,所以在存储UmlElement的时候,也应该按照不同的类分类存储。

Uml002:

思路为从ElementContainer中读取每一给类中的所有Attribute,再将所有的AssociationEnd变量,与Attribute存在一起,代码如下:

            ArrayList<String> lines = associationEnd.get(id);//将association加入attribute中
            HashMap<String, Integer> isVieded = new HashMap<>();
            for (String line : lines) {
                if (isVieded.containsKey(line)) {
                    continue;
                }
                isVieded.put(line, 1);
                if (!association.get(line).get(0).equals(id)) {
                    addAttributes(attributes, 0, line);
                } else if (!association.get(line).get(1).equals(id)) {
                    addAttributes(attributes, 1, line);
                } else {
                    addAttributes(attributes, 0, line);
                    addAttributes(attributes, 1, line);
                }
            }
            for (String eachName : attributes.keySet()) {//判断是否有重复
                if (attributes.get(eachName) > 1) {
                    pairs.add(new AttributeClassInformation(eachName, name));
                }
            }

Uml008:

通过递归的方式分别对所有类和接口进行检测。选定一个类(接口),如果在遍历过程中,再次访问到该类(接口),则说明存在循环继承情况。

    private boolean ifCircle(String originId, String id, boolean check, //判断是否循环继承
                             ArrayList<String> isVieded) {
        if (check && originId.equals(id)) {
            return true;
        }
        if (genera.containsKey(id)) {
            for (String each : genera.get(id)) {
                if (isVieded.contains(each)) {
                    continue;
                } else {
                    isVieded.add(each);
                    if (ifCircle(originId, each, true, isVieded)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

Uml009:

与循环继承类似,同样用递归的方式便利所有类。但要注意的是,可能会在接口实现的过程中重复实现某一接口,但并没有重复继承,如:

 

 所以检测重复继承的时候不能简单的把类和接口分开考虑。代码如下:

        for (String id : container.keySet()) {//检测是否出现接口重复实现的情况
            ArrayList<String> fathers = new ArrayList<>();
            for (UmlElement element : container.get(id)
                    .getInterfaceRealization()) {
                String each = element.toJson().get("target").toString();
                if (fathers.contains(each) || idList.contains(each)) {
                    idList.add(id);
                    list.add((UmlClassOrInterface)container
                            .get(id).getUmlElement());
                } else {
                    fathers.add(each);
                    if (ifDuplicate(idList, each, fathers)) {
                        idList.add(id);
                        list.add((UmlClassOrInterface)container
                                .get(id).getUmlElement());
                    }
                }
            }
        }
    private boolean ifDuplicate(ArrayList<String> idList,//检测是否重复继承
                                String id, ArrayList<String> fathers) {
        if (!genera.containsKey(id)) {
            return false;
        }
        for (String each : genera.get(id)) {
            if (fathers.contains(each) || idList.contains(each)) {
                return true;
            } else {
                fathers.add(each);
            }
            if (ifDuplicate(idList, each, fathers)) {
                return true;
            }
        }
        return false;
    }

总的来说,本单元的作业并不需要在算法上做过多的考量,但相应的,在分析UML图上需要画的时间会很多,个人认为,本单元的作业在分析上花的时间最多(其实就是边学UML边分析)。

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

本学期oo课程,一共包括求导、电梯调度、JML语言的探索以及UML工具的探索四个单元的实验。第一个单元的难点在于正则表达式以及抽象层次,由于是第一次接触面向对象程序,所以作业完成得并不太理想,主要体现在每一次作业几乎都需要重构,而且,所说是面向对象程序,但是却并没有用面向对象的方式处理。第二个单元的主要锻炼的是多线程编程,并接触了几种常用的模式(生产者/消费者的模式,发布/订阅者模式,单例模式)。第三单元在如何设计架构以及如何降低代码的复杂度上面需要做很多考量,这一次的作业由于完成时间比较短,并没有在架构上仔细思考,所以成绩并不好。第四单元的作业则重在分析,由于在前文有分析,故不做赘述。

在完成每个单元作业的过程中,可以感觉到每次代码迭代的工作量越来越少,在后面几次作业中,完成代码的过程中,除了实现本次应实现的功能,还考虑了怎样写才能更利于下一次作业的扩展。相比于第一单元的时候,每写一次作业都需要重构代码。在完成代码的过程中,更加注重架构的设计以及代码的可扩展性。同时,对于OO方法的理解也得到了一定程度的提高,从开始的“面向过程式”的程序到理解面向对象的思想。

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

第一个单元的测试主要是空想,将可能出现错误的数据列出来,与同学分享数据,检测程序。在互测环节中,由于有多份代码,单个输入比较浪费时间。所以可以将每一份代码预先编译好,放在同一个目录下用脚本进行测试。但是,在第二次特别是比三次作业中,由于每份代码对表达式的化简程度不一样,所以在检测正确性上比较麻烦。

第二个单元的测试比较难相比第一个单元而言困难了不少,数据往往都是在0时刻输入。另外,构造测试数据时,除了随机生成,还应该考虑电梯的停靠楼层,构造特殊的测试数据以发现程序中的bug。

第三单元比较注重的是单元化测试,来构造测试数据,可对每个方法进行有针对性的测试,这样可以帮助我们快速定位bug,另外在定位bug的过程中需要多使用调试工具。但是这个单元的作业在通过中测后并没有进行太多测试,也导致了成绩不理想。所以测试环节和架构设计同等重要。

第四个单元的作业由于没有互测,所以在测试上花的时间相对较少。构造测试数据的方法是通过StarUml画UML图,再生成数据。在画图过程中需要尽量多的考虑特殊情况,避免遗漏。

四、课程总结

总的来说,这学期的OO学习有不少收获(虽然找bug的过程确实很痛苦),从开始的连Java程序都不会写,到如今能在两天内完成几百行代码。从开学到考试,几乎每一周都要完成一次OO作业,并且伴随着作业还有互测环节和bug修复环节。整个学期写的代码也有近万行,相比学期初的代码,现在无论是再架构上还是在代码风格,复杂度上都有了不小的进步。此外,我还了解了很多实用的设计模式,并且在作业中实际的应用了这些设计模式,比起简单的理论层面上的了解学习,这种实践与理论结合的方式能更好的接受知识。另外,由于互测的需要,我也尝试自己写测试程序或者用脚本进行测试,也是成长。

除了在写代码方面的提升,在思维方面的成长也很重要,除了能在完成作业的过程中学习,还能通过研讨课上与同学交流获得提升。

五、课程意见

1、希望每一次作业完成之后都能选出几份优秀代码供学习参考,因为很多人在互测环节中都注重于找bug而没有去分析别人的代码。所以贴出优秀代码能让我们更好的意识到自己的缺点并学习改正。

2、关于课上测试,希望有一定的反馈,可以是在测试结束之后,以便我们找到并改正自己的错误。

3、关于互测,希望对上传的互测数据加上一定的限制,避免一组数据重复提交。

posted @ 2019-06-23 15:43  17373462  阅读(145)  评论(0编辑  收藏  举报