首页 > 其他分享 >软件构造中的各类注释介绍与实例演示

软件构造中的各类注释介绍与实例演示

时间:2024-04-09 18:11:55浏览次数:27  
标签:演示 Spec 注释 实例 Safety Invariant spec Exposure

本文系笔者在学习软件构造课程期间所写,不保证通用性和正确性,仅供参考。

目录

  1. 前言
  2. Spec
  3. Abstract Function
  4. Rep Invariant
  5. Safety from Exposure
  6. Testing Strategy
  7. 结语

一、前言

之前的代码中都不怎么重视注释,有注释也就是随便一两行解释一下下面的代码是干什么的。学习课程之后了解到软件构造中有很多约定好的注释格式,本文就把这些格式进行总结归纳,并在最后用一个例子来体现它们在具体应用中是怎么体现的。

本文所用的语言为课程要求的Java,IDE为IntelliJ IDEA。


二、Spec

Spec的全称为(Speculation) Specification,即代码规约,一般用在方法前,注明方法的各类信息,也是方法向用户提供信息的唯一途径。

2.1 写Spec的好处

  • 增加代码可读性
  • 对代码编写提出约束,在多人项目中方便合作
  • 便于用户使用
  • 作为文档注释生成Javadoc
  • 我在spec里要求了你的数据必须以什么约束输入进来,那你要是不守规定出问题了就不关我的事啦x

2.2 Spec内容

Java中的spec如下所示:

/**
 * 方法描述
 * @param 参数 对参数的介绍和约束
 * @return 对返回值的介绍
 * @throws 描述方法可能抛出的异常
 */

@xxx 为spec中的属性,常用的即上面所见的三种,除此之外还有@author(注明方法作者)等。一些属性可以有多个,比如多个@param介绍不同的参数等。以@开头是为了让Javadoc识别并生成文档。

2.3 Spec生成

Java中注释分为三类:单行注释(//)、多行注释(/*)、文档注释(/**)。即Java会对/**格式的注释有额外的处理。

在IDEA中,默认只需在一个方法前打出/**并回车就可以自动识别方法的相关内容并生成格式化的spec。如下所示:

/**
int findIndex(String str) throws IllegalArgumentException {}

按下回车后:

/**
 * 
 * @param str
 * @return 
 * @throws IllegalArgumentException
 */
int findIndex(String str) throws IllegalArgumentException {}

值得一提的是,Java的spec支持HTML语法,在描述中插入HTML标志,可以使spec看起来更工整、美观。

2.4 Spec的前置/后置条件、强弱比较

Spec的前置条件是对客户端的约束,即在使用方法时必须满足的条件,比如@param;
而后置条件是对开发者的约束,即方法结束时必须满足的条件,比如@return,@throws。

称两个方法是等价的,如果它们虽然实现方式不同,但是可以应用相同的规约。

Spec也有强弱之分:如果一个spec的前置条件更弱,而后置条件更强,就意味着这个spec更强。

用通俗的话来说,就是这个spec更好用了。例如设一个函数findIndex(String str),它的功能是找到字符'a'在str中的位置:

原来的spec可能是:

@param str中必须含有'a'
@return 所有'a'中任意一个的位置

更强的spec可以是:

@param 任意字符串
@return 第一个'a'的位置,如果没有'a',返回-1

可见后者完全可以承担前者的工作,但它又可以实现一些其他的功能,所以说它更强。

还有一些情况不能比较两个spec的强弱,比如一个spec前置条件和后置条件都更弱/更强,或者二者的条件不完全一致(一个返回int,而另一个返回int[])。

2.5 关于Spec中的约束

我在spec里要求了你的数据必须以什么约束输入进来,那你要是不守规定出问题了就不关我的事啦x

确实是这样。但是结合2.4节就可以看到,约束过多的spec相应的就更弱,这样的spec对用户来说就更不好用了。所以虽然可以任意做出约束,但还是需要尽可能地提高spec的强度,以提高用户体验,提高方法竞争力。

还有一种情况,是出于程序自身的安全性考虑的。虽然约束代表你可以不用考虑约束之外的情况,但不代表用户就一定会乖乖照着约束来。如果约束之外的情况会导致程序本身内部泄漏等不安全的行为,这就不是在spec里可以禁止得了的了,而必须认真修复漏洞。


三、Abstract Function

接下来的三个注释都是以非文档注释形式进行的,也就是说这些是方法内部的注释,仅供研究源码使用。

Abstract Function,抽象函数,是一个由方法的内部域到外部表示的映射,它展示了方法内部的参数代表着什么。

3.1 Abstract Function内容

Abstraction Function可以写为如下形式:

// AF(某域) = 其表示的抽象实例

一般建议对类的所有内部域都写出它的AF(),这样可以对数据表示有一个充分的掌握。

作为一个映射,Abstraction Function只能保证是满射。比如说一个字符串,内部实现可能是char[],那么AF()就表示某个状态下的char[]对应一个什么样的字符串。每个可表示的字符串都会有一个对应的char[];不同的char[]可以对应同一个String;也可以有无法对应一个字符串的char[]。由此可见AF()是满射,但未必是单射。


四、Rep Invariant

Rep Invariant,全写为Representation Invariant,表示不变量。

上文说到AF()是由方法的内部域到外部表示的映射,而Rep Invariant则是表明怎样的内部域可以进行这样的映射。

RI()是一个从内部实现到boolean的映射,合法的值映射为true,反之为false。

4.1 Rep Invariant内容

Rep Invariant可以写为如下形式:

// Rep Invariant:
//  str.length() > 0;

值得注意的是,如果某个域完全没有约束要求,那么RI应该填true。

4.2 checkRep()

虽然注释是这么写了,可怎么就能保证这些域就一定会在约束内呢?这就需要在类的内部具体实现一个函数:checkRep()

checkRep()其实就是RI的实现,一般会放在类中每个方法的末尾,检查经过这么一个方法的操作之后,各变量是否还满足RI()。由于是测试用的函数,一般是用assert检测或者在不合规时抛出异常,而不是真的返回一个boolean。

五、Safety from Exposure

在2.5节中提到了内部泄漏的问题。这当然也是代码编写中的一个重要话题,所以就有了Safety from Exposure,自证清白的一种注释。

Safety from Exposure主要是阐明你做了什么工作来避免出现内部泄漏。

5.1 Safety from Exposure内容

Safety from Exposure可以写为如下形式:

// Safety from Exposure:
//  All fields are private and ..., the methods...

一般会把上面的三种注释写在一起,放在内部域之下,所有方法之上,如下所示:

// Abstract Function:
//  AF(xxx) = ...
// Rep Invariant:
//  true
// Safety from Exposure:
//  ...

5.2 避免泄露的tips

一些可能经常会写在Safety from Exposure里的,避免泄漏的基础的技巧:

  • 将所有内部域设为private,尽可能设为final;
  • 尝试选择不可变类型,如String而非StringBuilder;
  • 方法返回可变的内部值时,考虑会不会把其地址泄漏出去:比如内部有个ArrayList list,则return new ArrayList (list)而非直接return list。
  • 直接将整个类写为不可变的。举例而言,实现Pair<String, Integer>中改变Integer的方法,并非直接改变Integer的值而是构造出一个新的Pair而遗弃原来的Pair。这种思想需要考虑实际情况。

六、Testing Strategy

Java中有完备的JUnit用来对程序进行测试,那么为了提高测试代码的可读性,也需要在测试文件中写必要的注释,这就是Testing Strategy,测试策略。

6.1 Testing Strategy内容

Testing Strategy可写为如下形式:

// Testing Strategy:
//  Method1():
//    ......
//  Method2():
//    ......

一般写在测试方法之前。

6.2 进行测试的tips

测试要讲究策略,以尽可能发现程序中所有潜在的bug。下面是一些基础的测试策略,可以写在Testing Strategy里:

  • 对所有输出进行测试,不仅包括return,还有异常的抛出是否符合预期。
  • 考虑所有可能的输入情况,比如null、非法输入(要求int却输了char)等,并将各个参数的这些可能情况进行排列组合,穷尽可能。
  • 考虑极限值。常见的极限值有0、1、大数(甚至数据溢出)等情况。
  • 用到测试覆盖度的插件,观察已有的测试是否覆盖了程序中的所有代码(当然也不是必须全部覆盖)

七、结语

上述的五种注释便是常见的程序内注释了。当然注释可以很随意,比如在代码之间插入的解释注释,也是必要且未尝不可的;但加上这些约定俗成的注释,更有利于团队协作的效率,也很大程度避免了“跨月如读天书”的情况。不过,所有理论最后都是要根据实际情况进行调整的。实用主义,而不能太死板。

标签:演示,Spec,注释,实例,Safety,Invariant,spec,Exposure
From: https://www.cnblogs.com/Senolytics/p/18120214

相关文章

  • Java 实例 - 在控制台上随机得到两个数之间的任意整数值(Random随机数)
       在控制台上随机得到4~11之间的任意值,含4和11 1packagecom.guyu.demo;23importjava.util.Random;45/**6*7*2024年4月9日上午10:39:438*@authorGuyu9*10*随机数示例:11*在控制台上随机得到4~11之间的任意......
  • Python爬虫实例
    使用正则表达式和爬虫爬虫实例一:#第好几个方法实例importrequests#先导入爬虫的库,不然调用不了爬虫的函数importre#下面是可以正常爬取的区别,更改了User-Agent字段headers={"User-Agent":"Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/53......
  • GpuMall智算云很多用户问的:如何在实例后台运行训练或任务【限时】
    检验#GpuMall智算云#实例创建操作的时候来了,提供最简单的实例操作流程,简化运行步骤,提高训练速度,#算力租赁#立即免费体验:https://gpumall.com/login?type=register&source=cnblogs在正常情况下,使用命令pythontrain.py运行机器学习的训练或推理任务时,该进程会挂载到系统的前......
  • Draggable 拖拽实例
    <!DOCTYPEhtml><html> <head> <metacharset="utf-8"> <title>课程表拖拽</title> <styletype="text/css"> h1{ text-align:center; } .container{ display:flex; } .left......
  • Linux mformat命令教程:MS-DOS文件系统的磁盘格式化工具(附实例详解和注意事项)
    Linuxmformat命令介绍mformat是一个用于在低级格式化的磁盘上添加MS-DOS文件系统的命令。它可以在已经通过Unix低级格式化的磁盘上添加一个最小的MS-DOS文件系统(包括引导扇区、FAT和根目录)。Linuxmformat命令适用的Linux版本mformat命令在大多数Linux发行版中都可以使......
  • 【译】使用最新预览版查看您的拉请求注释
    在17.10预览版2中,我们刚刚发布了预览支持,可以直接在VisualStudio的工作文件中查看GitHub和AzureDevOps的拉取请求注释。作为开发者社区中最受欢迎的Git工具特性建议之一,我们需要您的帮助来确保我们在正确的轨道上!开始使用PullRequestComments1确保您的V......
  • WPF —— 平移变换动画实例
    创建动画面临第一个问题选择正确的属性,选择属性的准则有以下几个方式进行选择 1如果希望动画显示和隐藏元素的时候,不要使用visibility,应使用opacity进行动画 2如果改变元素的位置的动画,尽量使用Canvas,使用Canvas.left以及canvas.top属性进行调整 但是也可以使用thickne......
  • Java 实例 – 如何编译 Java 文件||Java 实例 – 如何执行编译过 Java 文件
    Java实例–如何编译Java文件本文我们演示如何编译HelloWorld.java文件,其中Java代码如下:publicclassHelloWorld{publicstaticvoidmain(String[]args){System.out.println("HelloWorld");}}接下来我们使用javac命令来编译Java文件,并......
  • MySQL分组查询实例
    DDL——学生表,课程表CREATETABLE`student`(`id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'学号',`createDate`datetimeDEFAULTNULL,`userName`varchar(20)DEFAULTNULL,`pwd`varchar(36)DEFAULTNULL,`phone`varchar(11)DEFAULTNULL,`age`......
  • 利用matlab的guide制作一个凯撒密码加解密演示界面
    第一步:在命令行窗口输入guide,回车选择新建gui如图所示,两个粉的是可编辑文本,一个按钮,三个写着字和一个白色的框是静态文本先把我们需要的这些东西都拉出来,数量记得到位,布局自己调粉色什么怎么调就自己探索一下,一般拉出来是白色双击其中一个静态文本,会弹出来一个对话框,......