一、梳理JML相关内容
JML是一种形式化的、面向JAVA的行为接口规格语言。JML允许在规格中混合使用Java语法成分和JML引入的语法成分、拥有坚实的理论基础、使用Javadoc的注释方式、且已经拥有了相应的工具链,可以自动识别和分析处理JML规格。JML的相关工具链包括OpenJML、JMLUnit等,OpenJML可以对JML的正确性进行静态检验,以及对方法的正确性进行验证;JMLUnit可以生成简单的、针对边界条件的测试样例来对方法进行检测。
JML的形式化意味着可以消除歧义、可以被程序读取、可以自动分析和推导。这样,不仅写代码的人可以准确读懂需求,测试人员可以从测试上透视功能。
但JML语言所表示的规格仅仅是确保正确性,在实现时应仔细理解再去实现,往往都能找到比规格语言更优的实现方法。
二、部署SMT Solver验证代码
SMT Solver是一种测试代码的工具,功能与JUnit相似,即通过SAT(布尔表达式可满足性理论)验证代码是否能够得到预期结果。
三、部署JMLUnitNG/JMLUnit测试代码
配置完成JUnit后,结合第一次作业写了一个小小的测试代码:
1 import com.oocourse.spec1.exceptions.EqualPersonIdException; 2 import com.oocourse.spec1.exceptions.EqualRelationException; 3 import com.oocourse.spec1.exceptions.PersonIdNotFoundException; 4 import com.oocourse.spec1.exceptions.RelationNotFoundException; 5 import org.junit.Assert; 6 import org.junit.BeforeClass; 7 import org.junit.Test; 8 9 import java.math.BigInteger; 10 11 public class TaskOneTest { 12 private static MyNetwork network = new MyNetwork(); 13 private static MyPerson myPerson = new MyPerson(1,"test",BigInteger.TWO,100); 14 15 @BeforeClass 16 public static void before() throws EqualPersonIdException { 17 network.addPerson(new MyPerson(1,"a",BigInteger.ONE,1)); 18 network.addPerson(new MyPerson(2,"b",BigInteger.ZERO,2)); 19 network.addPerson(new MyPerson(6,"c",BigInteger.ZERO,3)); 20 network.addPerson(new MyPerson(7,"d",BigInteger.ZERO,4)); 21 network.addPerson(new MyPerson(8,"e",BigInteger.ZERO,5)); 22 network.addPerson(new MyPerson(9,"f",BigInteger.ZERO,6)); 23 } 24 25 @Test 26 public void getId(){ 27 Assert.assertEquals(myPerson.getId(),1); 28 } 29 30 @Test 31 public void getName(){ 32 Assert.assertEquals(myPerson.getName(),"test"); 33 } 34 35 @Test 36 public void getCharacter(){ 37 Assert.assertEquals(myPerson.getCharacter(),BigInteger.TWO); 38 } 39 40 @Test 41 public void getAge(){ 42 Assert.assertEquals(myPerson.getAge(),100); 43 } 44 45 @Test 46 public void isLinked(){ 47 Assert.assertTrue(myPerson.isLinked(myPerson)); 48 } 49 50 @Test 51 public void getAcquaintanceSum(){ 52 Assert.assertEquals(myPerson.getAcquaintanceSum(),0); 53 } 54 55 @Test 56 public void queryValueInMyPerson(){ 57 Assert.assertEquals(myPerson.queryValue(myPerson),0); 58 } 59 60 @Test(expected = EqualPersonIdException.class) 61 public void addPerson() throws EqualPersonIdException { 62 network.addPerson(new MyPerson(1,"a",BigInteger.ONE,1)); 63 } 64 65 @Test(expected = PersonIdNotFoundException.class) 66 public void addRelationNoId() throws PersonIdNotFoundException, EqualRelationException { 67 network.addRelation(3, 2, 3); 68 } 69 70 @Test(expected = EqualRelationException.class) 71 public void addRelationEqualRelation() throws PersonIdNotFoundException, EqualRelationException { 72 network.addRelation(1,2,100); 73 network.addRelation(2,1,10); 74 } 75 76 @Test(expected = PersonIdNotFoundException.class) 77 public void queryValue() throws PersonIdNotFoundException, RelationNotFoundException, EqualRelationException { 78 network.addRelation(6,7,100); 79 int ans = network.queryValue(6,7); 80 Assert.assertEquals(ans,100); 81 int sameId = network.queryValue(1,1); 82 Assert.assertEquals(sameId,0); 83 network.queryValue(2,3); 84 } 85 86 @Test(expected = PersonIdNotFoundException.class) 87 public void queryConflict() throws PersonIdNotFoundException { 88 BigInteger ans = network.queryConflict(1,2); 89 Assert.assertEquals(ans,BigInteger.ONE); 90 network.queryConflict(2,3); 91 } 92 93 @Test(expected = PersonIdNotFoundException.class) 94 public void queryAcquaintanceSum() throws PersonIdNotFoundException, EqualRelationException { 95 network.addRelation(8,9,1); 96 int ansA = network.queryAcquaintanceSum(8); 97 int ansB = network.queryAcquaintanceSum(9); 98 Assert.assertEquals(ansA,1); 99 Assert.assertEquals(ansB,1); 100 network.queryAcquaintanceSum(3); 101 } 102 103 @Test(expected = PersonIdNotFoundException.class) 104 public void compareAge() throws PersonIdNotFoundException { 105 int ans = network.compareAge(1,2); 106 Assert.assertEquals(ans,-1); 107 int sameId = network.compareAge(1,1); 108 Assert.assertEquals(sameId,0); 109 network.compareAge(1,3); 110 } 111 112 @Test(expected = PersonIdNotFoundException.class) 113 public void compareName() throws PersonIdNotFoundException { 114 int ans = network.compareName(1,2); 115 Assert.assertEquals(ans,-1); 116 int sameId = network.compareName(1,1); 117 Assert.assertEquals(sameId,0); 118 network.compareName(1,3); 119 } 120 121 @Test 122 public void queryPeopleSum(){ 123 int ans = network.queryPeopleSum(); 124 Assert.assertEquals(ans,6); 125 } 126 127 @Test(expected = PersonIdNotFoundException.class) 128 public void queryNameRank() throws PersonIdNotFoundException { 129 int ans = network.queryNameRank(2); 130 Assert.assertEquals(ans,2); 131 network.queryNameRank(3); 132 } 133 134 @Test(expected = PersonIdNotFoundException.class) 135 public void isCircle() throws PersonIdNotFoundException, EqualRelationException { 136 network.addRelation(1,2,100); 137 boolean ans = network.isCircle(1,2); 138 Assert.assertTrue(ans); 139 network.isCircle(3,2); 140 } 141 }
四、作业架构梳理
三次作业的都采取了对着JML写代码的方法,往往在写的过程中才能理解JML规格是在干嘛。事实上,正确的顺序是先理解JML规格,再根据规格选择合适的容器和算法完成代码。因此三次作业的完成都不太理想。
第一次作业的难度不高,因此在算法上不必纠结太多。第二次作业出现了两个需要二重循环遍历才能得出结果的函数,直接按照JML写造成了强测的扑街,这也证明了架构的重要性:不能把各个函数分开看,而应该在整个类的层次上多加考虑。第三次作业出现了最短路径算法、边双连算法,直接照搬规格是走不通的,只能复习数据结构并写出代码。但是简单的实现算法在强测中再次扑街,应该多加思考优化算法。
五、分析bug修复情况
第一次的bug主要在于wrong answer,由于不能完全理解JML出现错误。
第二次bug主要在于CTLE,由于多次调用o(n^2)级别的代码导致运算量过大。通过优化架构,在输入时就遍历并存储,解决了问题。
第三次bug在于RTE,由于对几个函数的算法照搬JML,导致运行超时。应该结合整个类别,设计架构、优化算法。
六、心得体会
1、JML是规范代码的重要工具,对规格化编程有重要作用。
2、不同的算法对代码效率有很大影响,通过优化算法可以使程序获得脱胎换骨的变化。
3、即使规定了规格,代码架构仍然很重要!!!