OO第三次博客作业
-
理论基础
面向对象分析与设计的原则之一是程序化思维应尽可能地推迟。JML将这种思维扩展到方法设计阶段。参见这里 。JML 给我们提供了这样的工具,用来更明确地描述我们试图将一个方法实现什么功能,而不是去具体实现它。
JML遵从契约式设计的软件设计模式。
契约式设计或者Design by Contract (DbC)是一种设计计算机软件的方法。这种方法要求软件设计者为软件组件定义正式的,精确的并且可验证的接口,这样,为传统的抽象数据类型又增加了先验条件、后验条件和不变式。
无论是设计阶段,还是验证阶段,这样的描述都可以发挥出巨大作用,原因在于它迫使我们在最开始设计时,就考虑清楚每个方法的职责。一方面,它让设计的事情归设计管,代码的事情归代码管,让设计和写代码分开,让这两者的思路更清晰,更纯粹;另一方面,清楚地划分出各个方法地职能,可以避免一些调用方和被调用方都做检查的冗余编码的情况出现,更重要的是在检查错误时更加容易。
事实上,自然语言也可以做相同的事情。不过JML的描述会更精确,这在自动化测试和形式验证等方面会很有优势。
-
应用工具链文档
-
jml-launcher (the launcher for the graphical user interface tools).
-
-
jml and jml-gui (the checker).
-
jmlc and jmlc-gui (compile for runtime assertion checking).
-
jmldoc and jmldoc-gui (a version of javadoc that includes JML specification information).
-
jmle (compile for executing or prototyping specifications).
-
jmlrac (a version of java, the VM, that includes the bin/jmlruntime.jar file in the CLASSPATH, for running files compiled with jmlc).
-
jmlre (a version of java, the VM, that includes the runtime support needed for executing specifications, for running files compiled with jmle).
-
jmlspec and jmlspec-gui (generate skeleton specification files from Java source files).
-
jmlunit and jmlunit-gui (produce unit testing code stubs for use with JUnit).
-
jml-junit (a version of JUnit's swing user interface that includes the bin/jmlruntime.jar and bin/jmljunitruntime.jar files in the CLASSPATH, for running JML and JUnit based tests on files compiled with jmlc and test cases generated by jmlunit).
-
部署实验
java -jar jmlunitng.jar demo/Demo.java java -cp jmlunitng.jar demo/.java java -cp jmlunitng.jar demo/Demo_JML_Data/.java java -cp judge/openjml.jar -rac demo/Demo.java
架构梳理
-
第一次作业
基本没有可以自我发挥的余地。只有类 Path 和 PathContainer。谈不上有什么架构吧。只有一些局部优化像,Path作为一个不可变对象,hashcode只需要算一次。使用懒汉单例模式创建hashSet判断是否有Node。。
关于架构唯一可以提到的就是:没有事先考虑到给代码留有可扩展的余地。每次迭代时都是复制上次代码而非extend和override。
-
第二次作业
第一次意识到可以创建多个类来完成一个方法。但实现不是很好。架构有以下几个问题:
-
图的构建和图的子结构混在一起,并没有分开建模。例如底层实现用了这样的形式
HashMap<Integer, HashMap<Integer, HashSet<Path>>> map;
HashMap<Integer, HashMap<Integer, Integer>> map;
对此没有封装的意识其实是一种面向对象思维的缺失。这样的事无巨细的结构会显得很乱,无论是检查纠错,bug定位,代码复用对开发者而言都是很不友好的。真正好的结构应该能为开发者屏蔽掉一些复杂性,而不是带来更多的复杂性。 ——沃兹基
-
图的构建和求最短路径的实现没有分开。事实上,图应该是较为静态的东西,而算法实现允许有一定程度变动来应对不同情景。这种策略机制分离,动态和静态分离的思路给代码带来更高的可复用性。
-
没有事先考虑到给代码留有可扩展的余地。每次迭代时都是复制上次代码而非extend和override。
-
-
第三次作业
架构是对第二次作业的将错就错,一错到底。
- 依旧采取了这样的形式 `HashMap<Integer, HashMap<Integer, HashSet<Path>>> map;` `HashMap<Integer, HashMap<Integer, Integer>> map;` 的底层设计。一点都不OO
- 唯一值得一提的地方是,用到了接口实现了类似于函数指针的东西,从而实现了“三种最小”的统一建模。所有子类必须实现`calEdgeWeight(int nodeId1, int nodeId2);`和`calTransferWeight(int nodeId1, int nodeId2);` 两个方法。
- 依旧存在第二次作业所存在的问题。直到看到标程才明白自己的上述问题。 -
重构?
没有重构,架构是一路烂到底。
只能靠对拍器苟活着。看完标程大概有一些感悟吧。一些随手记的日常:1,任务分层次很重要。一个任务往往不是由一个单独的类完成的。就像这次地铁的作业。图的构建可以拆开,底层的node,edge也可以封装起来。计算可以封装起来。 1.1,底层支撑性的结构 和 任务主要矛盾点应该分开。例如: 对于图构建,修改这一任务,node,edge就是底层支撑性的东西。可以让结构看起来很清晰。
1.2,OS老师经常教导我们操作系统不要和硬件绑得太死。OO也是这样。对于计算点点最短路径这一任务,图就是底层支撑性的东西。不同实现的图,只要提供相同的接口。不同的最短路径算法也不需要和图结构绑定死。
思考自己的代码,就是把所有的东西都装在脑子里,这样会有很乱的感觉。没有人可以同时把握住这么多的东西。真正优秀的面向对象应该要帮助人屏蔽掉一些复杂性,而不是带来更多的复杂性。
2,一个方法能做的东西,可以封装一个类去做。 3,一个包能做的事情,可以把内些会变动的放在一个包里,把不会变动的放在另一个包里。例如,图的实现是相对静态的。而计算最短路径算法是可以变动的
bug修复
第一次作业中的 compareTo 在减法溢出上翻了车。后来学乖了,就写了对拍器。
心得体会
-
契约式的模式,给设计带来很多的好处。像最开始说的那样,无论是设计阶段,还是验证阶段,这样的规格描述都可以发挥出巨大作用,原因在于它迫使我们在最开始设计时,就考虑清楚每个方法的职责。一方面,它让设计的事情归设计管,代码的事情归代码管,让设计和写代码分开,让这两者的思路更清晰,更纯粹;另一方面,清楚地划分出各个方法地职能,可以避免一些调用方和被调用方都做检查的冗余编码的情况出现,更重要的是在检查错误时更加容易。
-
但其实也并不局限用JML来描述,如果我们的最终目的仅仅是形成清晰的设计架构,明确的责任划分而不是形式化验证。自然语言其实可以省去很多不必要的麻烦。
-