1.编程语言中的功能/方法
2.规约:便于交流的编程,为何须要规约
行为等同规约结构:前提条件和后条件测试和验证规约
3.设计规约分类规约图表规约质量规约
4.总结程序员
方法:构建模块
大型项目由小型方法构建
方法能够单独开发,测试和重复使用
方法的用户不须要知道它是如何工做的 - 这被称为“抽象”算法
注意:调用方法时参数类型不匹配 - 静态检查
返回值类型是否匹配,也在静态类型检查阶段完成编程
(1)编程中的文档安全
Java API文档:一个例子
类层次结构和实现的接口列表。
直接子类,并为接口实现类。
类的描述
构建思想
方法摘要列出了咱们能够调用的全部方法
每一个方法和构造函数的详细描述数据结构
记录假设编程语言
向下写入变量的类型记录了一个关于它的假设:例如,此变量将始终引用一个整数。函数
声明一个变量final也是一种形式的文档,声明该变量在初始赋值后永远不会改变。工具
如何函数/方法的假设?性能
便于交流的编程测试
为何咱们须要写下咱们的假设?
程序必须记住两个目标:
黑客与工程
黑客每每以肆无忌惮的乐观为标志
但软件工程不是黑客行为。 工程师是悲观主义者:
静态检查有助于此
(2)规约和契约(方法)
规约(或称为契约)
规约是团队合做的关键。 没有规约就不可能委托实施方法的责任。
规约做为一种契约:实施者负责知足契约,而使用该方法的客户能够依赖契约。
规约对双方都有要求:当规约有先决条件时,客户也有责任。
为何规约?
现实:
优势:
规约对于方法的实现者来讲是很好的,由于他们给了实现者自由地改变实现而不告诉客户。
规约也可使码代码更快。
契约充当客户和实施者之间的防火墙。
对象与其用户之间的协议
该方法作了什么,而不是如何作
(3)行为等价性
要肯定行为等同性,问题是咱们是否能够用另外一个实现替代另外一个实现
等价的概念在客户眼中。
为了使一个实现替代另外一个实现成为可能,而且知道什么时候能够接受,咱们须要一个规约来讲明客户端依赖于什么
注意:规约不该该谈论方法类的局部变量或方法类的私有字段。
(4)规约结构:前提条件和后置条件
一个方法的规约由几个子句组成:
先决条件是客户(即方法的调用者)的义务。 这是调用方法的状态。
后置条件是该方法实施者的义务。
若是前提条件适用于调用状态,则该方法必须遵照后置条件,方法是返回适当的值,抛出指定的异常,修改或不修改对象等等。
总体结构是一个合乎逻辑的含义:若是在调用方法时前提条件成立,则在方法完成时必须保持后置条件。
若是在调用方法时前提条件不成立,则实现不受后置条件的限制。
Java中的规约
Java的静态类型声明其实是方法的前提条件和后置条件的一部分,该方法是编译器自动检查和执行的一部分。
静态检查
契约的其他部分必须在该方法以前的评论中进行描述,而且一般取决于人类对其进行检查并予以保证。
参数由@param子句描述,结果由@return和@throws子句描述。
将前提条件放在@param中,并将后置条件放入@return和@throws。
可变方法的规约
若是效应没有明确说明输入能够被突变,那么咱们假设输入的突变是隐式地被禁止的。
几乎全部的程序员都会承担一样的事情。 惊喜突变致使可怕的错误。
惯例:
可变对象可使简单的规约/合约很是复杂
可变对象下降了可变性
可变对象使简单的合约变得复杂
对同一个可变对象(对象的别名)的屡次引用可能意味着程序中的多个地方 - 可能至关分散 - 依靠该对象保持一致。
按照规约说明,契约不能再在一个地方执行,例如, 一个类的客户和一个类的实施者之间。
涉及可变对象的契约如今取决于每一个引用可变对象的每一个人的良好行为。
做为这种非本地契约现象的一个症状,考虑Java集合类,这些类一般记录在客户端和实现者之间的很是明确的契约中。
对这样的全局属性进行推理的须要使得理解难度更大,而且对可变数据结构的程序的正确性有信心。
咱们仍然必须这样作 - 为了性能和便利性 - 可是为了这样作,咱们在bug安全方面付出了巨大的代价。
可变对象下降了可变性
可变对象使得客户端和实现者之间的契约更加复杂,而且减小了客户端和实现者改变的自由。
换句话说,使用容许更改的对象会使代码难以改变。
(5)*测试和验证规约
正式契约规约
Java建模语言(JML)
这是一个有优点的理论方法
缺点
文本说明 - Javadoc
实用方法
记录每一个参数,返回值,每一个异常(选中和未选中),该方法执行的操做,包括目的,反作用,任何线程安全问题,任何性能问题。
不要记录实施细节
语义正确性遵照契约
编译器确保类型正确(静态类型检查)
静态分析工具(如FindBugs)能够识别许多常见问题(错误模式)
可是,如何确保语义的正确性?
正式验证
使用数学方法证实正式规约的正确性
正式证实一个实现的全部可能的执行符合规约
手动努力; 部分自动化; 不能自动肯定
测试
使用受控环境中的选定输入执行程序
目标
黑盒测试:以独立于实现的方式检查测试的程序是否遵循指定的规约。
(1)按规约分类
比较规约
它是如何肯定性的。 该规约是否仅为给定输入定义了单个可能的输出,或容许实现者从一组合法输出中进行选择?
它是如何声明的。 规约是否只是表征输出的结果,仍是明确说明如何计算输出?
它有多强大。 规约是否只有一小部分法律实施或一大套?
“什么使一些规约比其余规约更好?”
如何比较两种规约的行为来决定用新规约替换旧规约是否安全?
规约S2强于或等于规约S1若是
那么知足S2的实现也能够用来知足S1,在程序中用S2代替S1是安全的。
规则:
若是S3既不强于也不弱于S1,则规约可能会重叠(所以存在仅知足S1,仅S3,以及S1和S3的实现)或者可能不相交。
在这两种状况下,S1和S3都是没法比较的。
(2)图表规约
这个空间中的每一个点表明一个方法实现。
规约在全部可能的实现的空间中定义了一个区域。
一个给定的实现要么按照规约行事,要知足前置条件 - 隐含 - 后置契约(它在区域内),或者不(在区域外)。
实现者能够自由地在规约中移动,更改代码而不用担忧会破坏客户端。
这对于实现者可以提升其算法的性能,代码的清晰度或者在发现错误时改变他们的方法等而言是相当重要的。
客户不知道他们会获得哪些实现。
当S2比S1强时,它在此图中定义了一个较小的区域。
较弱的规约定义了一个更大的区域。
强化实施者的后置条件意味着他们自由度较低,对产出的要求更强。
弱化前提意味着:实现必须处理先前被规约排除的新输入。
(3)设计好的规约
规约的质量
什么是一个好方法? 设计方法意味着主要编写一个规约。
关于规约的形式:它显然应该简洁,清晰,结构良好,以便阅读。
然而,规约的内容很难规定。 没有一个可靠的规则,但有一些有用的指导方针。
规约应该是连贯的(内聚的)
该规约不该该有不少不一样的状况。 冗长的参数列表,深层嵌套的if语句和布尔型标志都是麻烦的迹象。
除了可怕地使用全局变量和打印而不是返回以外,规约不是一致的 - 它执行两个不一样的事情,计算单词并找出最长的单词。
调用的结果应该是信息丰富的
若是返回null,则没法肯定密钥是否先前未绑定,或者其实是否绑定为null。这不是一个很好的设计,由于返回值是无用的,除非您肯定没有插入null。
规约应该足够强大
规约应给予客户在通常状况下足够强大的保证 - 它须要知足其基本要求。 - 在规定特殊状况时,咱们必须格外当心,确保它们不会破坏原本是有用的方法。例如,对于一个不合理的论证抛出异常,但容许任意的突变是没有意义的,由于客户端将没法肯定实际发生了什么样的突变。
规约也应该足够薄弱
这是一个很差的规约。
规约应该使用抽象类型
用抽象类型编写咱们的规约为客户和实现者提供了更多的自由。
在Java中,这一般意味着使用接口类型,如Map或Reader,而不是像HashMap或FileReader这样的特定实现类型。
这强制客户端传入一个ArrayList,并强制实现返回一个ArrayList,即便可能存在他们但愿使用的替代List实现。
先决条件仍是后置条件?
是否使用前提条件,若是是,则在继续以前,方法代码是否应该尝试确保先决条件已知足?
对于程序员:
若是检查一个条件会使方法变得难以接受,那么一般须要一个先决条件。
对用户而言:
因此方法的用户不喜欢先决条件。
关键因素是检查的费用(编写和执行代码)以及方法的范围。
若是只在类本地调用,则能够经过仔细检查调用该方法的全部类来解决前提条件。
若是该方法是公开的,而且被其余开发人员使用,那么使用前提条件将不太明智。 相反,像Java API类同样,您应该抛出一个异常。
总结
规约做为程序实现者与其客户之间的关键防火墙。
它使得单独的开发成为可能:客户端能够自由地编写使用该过程的代码,而无需查看其源代码,而且实现者能够自由地编写实现该过程的代码而不知道它将如何使用。
减小错误保证安全
准备好改变
声明性规约在实践中是最有用的。
先决条件(削弱了规约)使客户的生活更加艰难,但明智地应用它们是软件设计师的重要工具,容许实施者作出必要的假设。
减小错误保证安全
容易理解
准备好改变