我正在学习前提条件以及何时使用它们。有人告诉我 前提条件
@pre fileName must be the name of a valid file
不适合以下代码:
/**
Creates a new FileReader, given the name of file to read from.
@param fileName- the name of file to read from
@throw FileNotFoundException - if the named file does not exist,
is a directory rather than a regular file, or for some other reason cannot
be opened for reading.
*/
public FileReader readFile(String fileName) throws FileNotFoundException {
. . .
}//readFile
为什么会这样?
编辑:另一个例子
我们假设以下,作为一个例子,以“正确”的方式完成。 请注意IllegalArgumentException和前提条件。注意行为如何 很明确,以及如何进行投掷声明 设定前提条件。最重要的是,请注意不包含的内容 NullPointerException的前提条件。再一次,为什么不呢?
/**
* @param start the beginning of the period
* @param end the end of the period; must not precede start
* @pre start <= end
* @post The time span of the returned period is positive.
* @throws IllegalArgumentException if start is after end
* @throws NullPointerException if start or end is null
*/
public Period(Date start, Date end) f
这些例子是否避免使用额外的前提条件?有人可以 认为如果我们避免先决条件,那么为什么要这样做呢? 也就是说,为什么不用@throws声明替换所有前置条件(如果 避免它们就是这里要做的事情)?
答案 0 :(得分:4)
维基百科将前置条件定义为:
在计算机编程中,前提条件是条件或谓词,在执行某些代码段之前或在正式规范中的操作之前必须始终为真。
如果违反前提条件,则代码部分的效果变得不明确,因此可能会或可能不会执行其预期的工作。
在您的示例中,如果文件名无效的方法的效果是定义的(它必须抛出FileNotFoundException
)。
换句话说,如果file
有效是一个先决条件,我们就知道它总是有效的,并且抛出强制执行异常的合同部分是不会永远不适用的。无法访问的规范案例就像代码无法访问代码一样。
修改强>:
如果我有一些先决条件,并且可以为这些条件提供定义的行为,那么如果我这样做会不会更好?
当然,但它不再是霍尔定义的先决条件。从形式上讲,一个方法具有前置条件pre
和后置条件post
意味着每次执行从状态prestate
开始并以状态poststate
结束的方法
pre(prestate) ==> post(poststate)
如果暗示的左侧是假的,无论poststate
是什么,这都是真实的,即该方法将满足其合约而不管它是什么,即方法的行为未定义。
现在,快进到现代,方法可以抛出异常。对异常建模的通常方法是将它们视为特殊的返回值,即异常是否是后置条件的一部分。
但异常并非真的无法到达,是吗?
如果throws子句是postcondtion的一部分,你有类似的东西:
pre(prestate) ==> (pre(prestate) and return_valid) or (not pre(prestate) and throws_ exception)
在逻辑上等同于
pre(prestate) ==> (pre(prestate) and return_valid)
也就是说,你是否写了throws子句并不重要,这就是为什么我称这个规范案例无法访问。
我想说,例外情况可以作为前提条件的补充,告知用户如果他/她违反合同将会发生什么。
没有;抛出条款是合同的一部分,如果合同被破坏,则不会产生任何影响。
当然可以定义需要满足@throws子句而不考虑前提条件,但这有用吗?考虑:
@pre foo != null
@throws IllegalStateException if foo.active
如果foo
为null
,是否必须抛出异常?在经典定义中,它是未定义的,因为我们假设没有人会为null
传递foo
。在你的定义中,我们必须在每个throws子句中明确重复:
@pre foo != null
@throws NullPointerException if foo == null
@throws IllegalStateException if foo != null && foo.active
如果我知道没有合理的程序员将null
传递给该方法,为什么我应该被强制在我的规范中指定该情况?它有什么好处来描述对调用者无用的行为? (如果调用者想知道foo是否为null,他可以自己检查它,而不是调用我们的方法并捕获NullPointerException!)。
答案 1 :(得分:3)
好的,这就是我发现的:
<强>背景强>
基于以下原则,如Bertrand Meyer的书中所述 面向对象的软件构建:
“非冗余原则” 情节应该是一个人的身体 常规测试常规 前提。“ - Bertrand Meyer
“前提条件可用性规则” 出现在前提条件下的特征 必须提供例程 例程的每个客户 可用。“ - Bertrand Meyer
,这两点回答了这个问题:
更多关于何时,为何以及如何使用前提条件:
“合同设计的核心是 想法,表示为非冗余 原则,任何一致性 可能危及a的条件 例程正常运作你 应该分配执行 条件只有两个中的一个 合同中的合作伙伴。哪一个? 在每种情况下,你有两个 可能性:•你指定了 对客户负责,其中 如果情况将作为一部分出现 例程的先决条件。 • 要么 您指定供应商,其中 情况将出现在一个 条件指令的形式如果 条件然后...,或等价物 控制结构,在例程中 体。
我们可以称之为第一种态度 要求和第二个 宽容。“ - Bertrand Meyer
因此,只有在决定客户承担责任时,才应存在先决条件。 由于服务器应 不 测试前置条件,因此行为将变为未定义(如维基百科上所述)。
<强>答案强>
@throws
声明暗示该方法具有(除了断言)
测试了前提条件。这违反了第二点。至于空指针;这表明分配了空指针的责任 到服务器。也就是说,使用“宽容的态度”,而不是“苛刻的态度”。 这完全没问题。如果一个人选择实施苛刻的态度,那么就会 删除throws声明(但更重要的是; 不测试它),然后添加 一个先决条件声明(也许是一个断言)。
答案 2 :(得分:2)
我认为按合同构思设计(我自己还没有使用)和前/后条件旨在保证从方法传入和传出的特定条件。特别是编译器(在这种情况下,理论上称为Java没有内置)需要能够验证合同条件。在文件前置条件的情况下,由于文件是外部资源,类可能会移动并且可能不存在相同的文件等,因此无法完成此操作。编译器(或预处理器)如何保证此类合同?
另一方面,如果您只是将它用于评论,那么它至少会向其他开发人员展示您的期望,但您仍然期望在文件未退出时会出现异常。
我认为它“不适合”合同设计的正式意义上的方法,因为即使是一个案例也无法验证。也就是说,您可以在一个环境中提供有效的文件名,但这可能在您的程序外部的其他环境中无效。
另一方面,日期示例,前置和后置条件可以在调用者上下文中验证,因为它们不受该方法本身无法控制的外部环境设置的影响。