zoukankan      html  css  js  c++  java
  • 面向对象第三单元总结

     (一)梳理JML语言的理论基础、应用工具链情况

    梳理JML语言的理论基础

    在JML官网上,是这样定义JML的。

    Java建模语言(JML)是一种行为接口规范语言,可用于指定Java模块的行为 它结合了Eiffel的契约方法设计 和Larch系列接口规范语言的基于模型的规范方法 ,以及细化演算一些元素 

    基础语法梳理

    https://blog.csdn.net/piaopu0120/article/details/89527175

    链接为我根据预习资料和第一次上课内容进行的JML语法整理,内容较多,仍放在另一个博客里。

    2.应用工具链

    • Open JML:OpenJML是Java程序的程序验证工具,允许检查Java Modeling Language中注释的程序的规范;
    • JML SMT Solver:OpenJML的主流SMT Solver;
    • JML Unit:单元测试工具;
    • JMLdoc:JML规范的javadoc(jmldoc)增强版本;
    • jmlc:断言检查编译器。

    (二)部署JMLUnitNG/JMLUnit,针对Graph接口的实现自动生成测试用例, 并结合规格对生成的测试用例和数据进行简要分析

    这个东西玄之又玄,成功的运行除了环境搭好外,还需要保证代码和逻辑的双重匹配。总之,感谢郑文帝对我的帮助。

    以下是针对MyGraph接口生成的测试用例:

    Failed: racEnabled()
    Passed: constructor MyGraph()
    Passed: <<MyGraph@4ca8195f>>.addPath(null)
    Passed: <<MyGraph@490d6c15>>.containsEdge(-2147483648, -2147483648)
    Passed: <<MyGraph@449b2d27>>.containsEdge(0, -2147483648)
    Passed: <<MyGraph@5479e3f>>.containsEdge(2147483647, -2147483648)
    Passed: <<MyGraph@27082746>>.containsEdge(-2147483648, 0)
    Passed: <<MyGraph@66133adc>>.containsEdge(0, 0)
    Passed: <<MyGraph@7bfcd12c>>.containsEdge(2147483647, 0)
    Passed: <<MyGraph@42f30e0a>>.containsEdge(-2147483648, 2147483647)
    Passed: <<MyGraph@24273305>>.containsEdge(0, 2147483647)
    Passed: <<MyGraph@5b1d2887>>.containsEdge(2147483647, 2147483647)
    Passed: <<MyGraph@46f5f779>>.containsNode(-2147483648)
    Passed: <<MyGraph@1c2c22f3>>.containsNode(0)
    Passed: <<MyGraph@33e5ccce>>.containsNode(2147483647)
    Passed: <<MyGraph@5a42bbf4>>.containsPathId(-2147483648)
    Passed: <<MyGraph@270421f5>>.containsPathId(0)
    Passed: <<MyGraph@52d455b8>>.containsPathId(2147483647)
    Passed: <<MyGraph@4f4a7090>>.containsPath(null)
    Passed: <<MyGraph@18ef96>>.getDistinctNodeCount()
    Failed: <<MyGraph@6f79caec>>.getPathById(-2147483648)
    Failed: <<MyGraph@67117f44>>.getPathById(0)
    Failed: <<MyGraph@5d3411d>>.getPathById(2147483647)
    Failed: <<MyGraph@2471cca7>>.getPathId(null)
    Passed: <<MyGraph@5fe5c6f>>.getShortestPathLength(-2147483648, -2147483648)
    Passed: <<MyGraph@6979e8cb>>.getShortestPathLength(0, -2147483648)
    Passed: <<MyGraph@763d9750>>.getShortestPathLength(2147483647, -2147483648)
    Passed: <<MyGraph@5c0369c4>>.getShortestPathLength(-2147483648, 0)
    Passed: <<MyGraph@2be94b0f>>.getShortestPathLength(0, 0)
    Passed: <<MyGraph@d70c109>>.getShortestPathLength(2147483647, 0)
    Passed: <<MyGraph@17ed40e0>>.getShortestPathLength(-2147483648, 2147483647)
    Passed: <<MyGraph@50675690>>.getShortestPathLength(0, 2147483647)
    Passed: <<MyGraph@31b7dea0>>.getShortestPathLength(2147483647, 2147483647)
    Passed: <<MyGraph@3ac42916>>.isConnected(-2147483648, -2147483648)
    Passed: <<MyGraph@47d384ee>>.isConnected(0, -2147483648)
    Passed: <<MyGraph@2d6a9952>>.isConnected(2147483647, -2147483648)
    Passed: <<MyGraph@22a71081>>.isConnected(-2147483648, 0)
    Passed: <<MyGraph@3930015a>>.isConnected(0, 0)
    Passed: <<MyGraph@629f0666>>.isConnected(2147483647, 0)
    Passed: <<MyGraph@1bc6a36e>>.isConnected(-2147483648, 2147483647)
    Passed: <<MyGraph@1ff8b8f>>.isConnected(0, 2147483647)
    Passed: <<MyGraph@387c703b>>.isConnected(2147483647, 2147483647)
    Failed: <<MyGraph@224aed64>>.removePathById(-2147483648)
    Failed: <<MyGraph@c39f790>>.removePathById(0)
    Failed: <<MyGraph@71e7a66b>>.removePathById(2147483647)
    Failed: <<MyGraph@2ac1fdc4>>.removePath(null)
    Passed: <<MyGraph@5f150435>>.size()
    
    ===============================================
    Command line suite
    Total tests run: 47, Failures: 9, Skips: 0
    ===============================================

    测试用例分析:

    对于int类型的数据,生成的大多数是0,边界的正负数据;

    对于object类型的数据,生成的大多是null。

    对于参数是object类型的数据,应该就是挺难解决的。因为自动测试样例不太好生成满足要求的新对象。但是对于int类型全是边界数据,我有点难以理解。

    (三)按照作业梳理自己的架构设计,并特别分析迭代中对架构的重构

    1.第九次作业

    没有做太多架构上的设计,仅完成了目标函数的补充。在细节方面的小优化是用hashset进行去重保存节点;用hashmap实现id和Path的双重查找。

    2.第十次作业

    依然没有做架构上的设计,新的图没有选择继承之前图,导致MyGraph很冗杂。主要实现了对于每一个Path的每一个小邻边的访存;修改了add和remove,完善功能;为求最短路,进行了floyd的初始化和实现。

    3.第十一次作业

     

    第三次作业因为有四个相似的图,所以进行了架构上的优化。

    虽然之前有考虑过指导书推荐的组合模式+工厂模式的架构,但当时主观觉得组合模式的树形结构不太好运用到这次的题(实际上是我建模的主体不对)。所以考虑了建造者模式。

    建造者模式(Builder Pattern)

    使用多个简单的对象一步一步构建成一个复杂的对象。

    主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

    用大白话讲就是生成一个套餐。

    我生成了一个floyd套餐builder和一个dij套餐builder,这两个builder可以生成各种各样的套餐。

    分析:

    ①把问题分成由floyd算法求解更优和dijkstra算法求解更优的两类

    ②把floyd和dijkstra的初始化与实现拆分成小块。

    ③将小块进行重新组合,实现builder。

    顶层组合实现举例:

     1 public Dij lessTransfer() {
     2         if (transDij == null) {
     3             transDij = new Dij();
     4             transDij.addStratege(new InitLessTransfer());
     5             transDij.addStratege(new NoneTransfer());
     6         }
     7         return transDij;
     8     }
     9 
    10     public Dij lessCost() {
    11         if (costDij == null) {
    12             costDij = new Dij();
    13             costDij.addStratege(new InitLessMoney());
    14             costDij.addStratege(new Transfer2());
    15         }
    16         return costDij;
    17     }
    18 
    19     public Dij lessUnplefation() {
    20         if (unpleDij == null) {
    21             unpleDij = new Dij();
    22             unpleDij.addStratege(new InitUnstatisfication());
    23             unpleDij.addStratege(new Transfer32());
    24         }
    25         return unpleDij;
    26     }

    调用builder实例:

     1 if (unpleFlag) {
     2            // 省略初始化代码
     3            // 如果没有缓存,调用builder1
     4             unpleDij = DijBuilder.getBuilder().lessUnplefation();
     5         } else {
     6             if (alreadyUnple.contains(fromNodeId)) {
     7                // 省略结果返回代码
     8             }
     9            // 如果有缓存,调用builder2
    10            unpleDij = DijBuilder.getBuilder().noneunpleDij();
    11         }
    12         // 省略更新缓存代码
    13         // 启动builder,得到结果
    14         unpleDij.runDij(fromNodeId, unpleGraph, unpleExist, distict,
    15             unpleNodeSort, buddy, pidMap, pathInformation);
    16 }

     下面是builder的具体实现:

    以不满意度为例

    DijBuilder

     1 public class DijBuilder {
     2     private Dij unpleDij = null;
     3     private Dij noneunpleDij = null;
     4 
     5     private static DijBuilder builder = new DijBuilder();
     6 
     7     private DijBuilder() {
     8     }
     9 
    10     public static DijBuilder getBuilder() {
    11         return builder;
    12     }
    13 
    14     public Dij lessUnplefation() {
    15         if (unpleDij == null) {
    16             unpleDij = new Dij();
    17             unpleDij.addStratege(new InitUnstatisfication());
    18             unpleDij.addStratege(new Transfer32());
    19         }
    20         return unpleDij;
    21     }
    22 
    23     public Dij noneunpleDij() {
    24         if (noneunpleDij == null) {
    25             noneunpleDij = new Dij();
    26             noneunpleDij.addStratege(new NoneInit());
    27             noneunpleDij.addStratege(new Transfer32());
    28         }
    29         return noneunpleDij;
    30     }
    31 
    32 }

    Dij

    public class Dij {
        private List<Strategy> strategies = new ArrayList<>();
    
        public void addStratege(Strategy s) {
            strategies.add(s);
        }
    
        public void runDij(int v0, int[][] distanceGraph, boolean[][] exist,
                           HashSet<Integer> distict,
                           HashMap<Integer, Integer> nodeSort,
                           HashMap<String, Integer> buddy,
                           HashMap<Path, Integer> pidMap,
                           PathMap[] pathInformation) {
            Strategy initMethod = strategies.get(0);
            initMethod.process(0, distanceGraph, exist, distict, nodeSort,
                buddy, pidMap, pathInformation);
            Strategy calMethod = strategies.get(1);
            calMethod.process(nodeSort.get(v0), distanceGraph, exist, distict,
                nodeSort, buddy, pidMap, pathInformation);
        }
    }

    Strategy接口

    1 public interface Strategy {
    2     void process(int v0, int[][] distanceGraph, boolean[][] exist,
    3                  HashSet<Integer> distict,
    4                  HashMap<Integer, Integer> nodeSort,
    5                  HashMap<String, Integer> buddy,
    6                  HashMap<Path, Integer> pidMap, PathMap[] pathInformation);
    7 }

    GenerateWay抽象类实现Strategy接口,Initial抽象类类似

     1 public abstract class GenerateWay implements Strategy {
     2 
     3     public abstract void process(int v0, int[][] distanceGraph,
     4                                  boolean[][] exist,
     5                                  HashSet<Integer> distict,
     6                                  HashMap<Integer, Integer> nodeSort,
     7                                  HashMap<String, Integer> buddy,
     8                                  HashMap<Path, Integer> pidMap,
     9                                  PathMap[] pathInformation);
    10 }

    Transfer32类继承抽象类GenerateWay

     1 public class Transfer32 extends GenerateWay {
     2 
     3     public void process(int v0, int[][] distanceGraph, boolean[][] exist,
     4                         HashSet<Integer> distict,
     5                         HashMap<Integer, Integer> nodeSort,
     6                         HashMap<String, Integer> buddy,
     7                         HashMap<Path, Integer> pidMap,
     8                         PathMap[] pathInformation) {
     9         StaticFun.dij(v0, distict.size(), 32, distanceGraph, exist);
    10     }
    11 }

    这样的方法使得在顶层实现不同的floyd或者dij的算法非常容易,可以实现n*m种任意的组合,并且不需要改动任何的底层代码。

    但在实现的过程中也会遇到一些困难:

      1. 统一接口导致传参过多;

      2. 没有办法在顶层进一步封装,使得顶层代码重复变多。

     

    (四)按照作业分析代码实现的bug和修复情况

    1. 第九次作业

    第九次作业被检测出来了一个bug,compareTO用了一种错误的方法,当时为了少些几行所以是通过差值判断的。这直接导致了溢出时判断错误。修复的时候把它修改成了大小比较。

    2. 第十次作业

    在判断最短路的时候忘记了两个相同点的情况,修复的时候把这个条件加上了。 

    3. 第十一次作业

    感谢莫策同学的数据生成器,经过很多数据的互拍让bug数减到最小。

    总结

    这些bug很不值得,他、它们都是很基础很容易用测试用例覆盖的问题。但这两次作业我都没怎么用心测试程序,导致了这两个bug的出现。第十一次作业我汲取了之前的教训,进行了大量的测试使得最后一次作业在强测互测都没有发现bug。以后一定谨记,在每一次测试之前优先考虑边界问题和特殊数据。

    (五)阐述对规格撰写和理解上的心得体会

    • 这几次作业中,在课上我们有机会写规格,课下我们主要是通过阅读规格。但是实际上,我在课下补充一个方法时,会不根据规格来撰写代码,只是会根据规格来作为debug的一层手段。我记得在第一次作业我准备按照规格来写代码的时候,发现使用的架构和数据结构与规格表述的不太适配。
    • 在后期阅读规格的时候,比如像计算容器类的不同节点个数或者最少换乘数路径这样的题目,用语言来描述很简单,但是它的逻辑用规格来书写就非常困难。写规格的感觉对于我来说特别像写离散数学证明题,需要用非常完备的逻辑来完善我们所想表达的。
    • 一个正确的规格有助于减少歧义,规格对于方法目的的说明非常有效,因为它是贴合数据代码的,挺像伪代码对于一个问题的说明功效。
    • 规格描述了数据的变与不变,忽略了中间过程的实现,是结果指向的。

    后话:

    这个单元的学习让我觉得挺迷的,因为JML的资料很少,对我来说环境安装和实际使用的可行性很低。虽然有同学能够很好的使用,但是有些时候这种成功不是所有人都能复刻的,迎接我们的可能还是一大堆不明所以的报错。这个单元的编程经历还是带给我了一些收获:

    1.架构上的设计

    这个单元对于架构上的设计思考得很多,每一个单元写优化的时候最先考虑的是优化架构。这个单元涉及到的图的访存还蛮复杂的,因为一直避免疯狂嵌套hashmap,所以想了很多办法。总的来说收获很多吧。

    2.规范性

    规格带给我的体验不是很多,但是能够从规格感受到代码规范的重要性。而且相较于前几次从头开始写,这几次主要是补充方法,由于父类接口的限制使写代码的时候要更好的设计。

    这个单元的第一次作业也是我第一次翻车,会好好反思的。

  • 相关阅读:
    EntityFramework优缺点
    领导者与管理者的区别
    七个对我最好的职业建议(精简版)
    The best career advice I’ve received
    Difference between Stored Procedure and Function in SQL Server
    2015年上半年一次通过 信息系统项目管理师
    Difference between WCF and Web API and WCF REST and Web Service
    What’s the difference between data mining and data warehousing?
    What is the difference between a Clustered and Non Clustered Index?
    用new创建函数的过程发生了什么
  • 原文地址:https://www.cnblogs.com/puublog/p/10886026.html
Copyright © 2011-2022 走看看