何时添加前置条件以及何时(仅)抛出异常?

发布于 2024-11-02 11:39:43 字数 1226 浏览 7 评论 0原文

我正在学习先决条件以及何时使用它们。有人告诉我 前提条件

@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 和前提条件。注意行为方式 定义良好,以及如何进行 throws 声明,即使 设定了一个前提条件。最重要的是,请注意它如何包含 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 声明(如果 避免它们就是这里所做的)?

I am learning about preconditions and when to use them. I have been told
that the precondition

@pre fileName must be the name of a valid file

does not suit in the following code:

/**
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

Why is this?

Edit: Another example

We are assuming that the following, as an example, is done the "right" way.
Note the IllegalArgumentException and the precondition. Note how the behavior
is well defined, and how the throws declaration is made even though
a precondition is set. Most importantly, notice how it doesn't contain
a precondition for the NullPointerException. Once again, why doesn't it?

/**
* @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

Are these examples avoiding use of extra preconditions? One could
argue that if we are avoiding preconditions, then why have them at all?
That is, why not replace all preconditions with @throws declarations (if
avoiding them is what is done here)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

青春如此纠结 2024-11-09 11:39:43

维基百科将前提条件定义为:

在计算机编程中,前置条件是在执行某些代码部分或正式规范中的操作之前必须始终为真的条件或谓词。

如果违反前提条件,则该代码段的效果将变得不确定,因此可能会也可能不会执行其预期工作。

在您的示例中,定义了文件名无效时该方法的效果(它必须抛出 FileNotFoundException)。

换句话说,如果 file 有效是一个先决条件,我们就知道它始终有效,并且合约中强制抛出异常的部分永远不会适用。无法到达的规范情况是一种代码味道,就像无法到达的代码一样。

编辑

如果我有一些先决条件,并且可以为这些条件提供定义的行为,那么这样做不是更好吗?

当然,但是这不再是霍尔定义的先决条件。正式来说,一个方法具有前置条件 pre 和后置条件 post 意味着该方法的每次执行都以状态 prestate 开始并以状态结束poststate

pre(prestate) ==> post(poststate)

如果蕴含的左侧为 false,则无论 poststate 是什么,这都是简单的 true,即该方法将满足其契约,无论它做什么,即该方法的行为未定义。

现在,快进到现代,方法可以抛出异常。对异常建模的通常方法是将它们视为特殊的返回值,即异常是否发生是后置条件的一部分。

不过,该异常并不是真的无法访问,不是吗?

如果 throws 子句是后置条件的一部分,那么您会得到类似这样的内容:

pre(prestate) ==> (pre(prestate) and return_valid) or (not pre(prestate) and throws_ exception)

这在逻辑上相当于

pre(prestate) ==> (pre(prestate) and return_valid)

,即,您是否编写那个 throws 子句并不重要,这就是为什么我称该规范情况不可访问。

我想说,例外更像是对前提条件的补充,告知用户如果他/她违反合同将会发生什么。

不; throws 条款是合同的一部分,因此如果合同被破坏,则没有任何影响力。

当然,可以定义无论前提条件如何都需要满足@throws子句,但这有用吗?考虑一下:

@pre foo != null
@throws IllegalStateException if foo.active

如果 foonull 是否必须抛出异常?在经典定义中,它是未定义的,因为我们假设没有人会为 foo 传递 null。在您的定义中,我们必须在每个 throws 子句中明确重复这一点:

@pre foo != null
@throws NullPointerException if foo == null
@throws IllegalStateException if foo != null && foo.active

如果我知道没有合理的程序员会将 null 传递给该方法,为什么我应该被迫在规范中指定这种情况?描述对调用者无用的行为有什么好处? (如果调用者想知道 foo 是否为 null,他可以自己检查,而不是调用我们的方法并捕获 NullPointerException!)。

Wikipedia defines precondition as:

In computer programming, a precondition is a condition or predicate that must always be true just prior to the execution of some section of code or before an operation in a formal specification.

If a precondition is violated, the effect of the section of code becomes undefined and thus may or may not carry out its intended work.

In your example, the effect of the method if the filename is invalid is defined (it must throw a FileNotFoundException).

Put differently, if file being valid were a precondition we'd know that it was always valid, and the part of the contract that mandates an exception is thrown it is wasn't would never apply. Unreachable specification cases are a code smell just like unreachable code.

Edit:

If I have some preconditions, and can provide defined behavior for these conditions, wouldn't it be better if I did so?

Of course, but then it's no longer a precondition as defined by Hoare. Formally speaking, that a method has precondition pre and postcondition post means that for each execution of the method that started in state prestate and ended in state poststate

pre(prestate) ==> post(poststate)

If the left hand side of the implication is false, this is trivially true irrespective of what poststate is, i.e. the method will satisfy its contract irrespective of what it does, i.e. the method's behaviour is undefined.

Now, fast forward to modern times, where methods can throw exceptions. The usual way to model exceptions is to treat them as special return values, i.e. whether an exception occurred is part of the postcondition.

The exception is not really unreachable though, is it?

If the throws clause is part of the postcondtion, you have something like:

pre(prestate) ==> (pre(prestate) and return_valid) or (not pre(prestate) and throws_ exception)

which is logically equivalent to

pre(prestate) ==> (pre(prestate) and return_valid)

that is, it doesn't matter whether you write that throws clause, which is why I called that specification case unreachable.

I would say that an exception rather works as a supplement to the precondition to inform the user of what's going to happen if he/she breaks the contract.

No; the throws clause is part of the contract, and as such carries no weight if the contract is broken.

Of course it is possible to define that @throws clauses need to be satisfied irrespective of the precondition, but is that useful? Consider:

@pre foo != null
@throws IllegalStateException if foo.active

Must the exception be thrown if foo is null? In the classic definition, it is undefined, because we assume nobody will pass null for foo. In your definition, we have to explicitly repeat that in every throws clause:

@pre foo != null
@throws NullPointerException if foo == null
@throws IllegalStateException if foo != null && foo.active

If I know no reasonable programmer is going to pass null to that method, why should I be forced to specify that case in my specification? What benefit does it have to describe behavior that is not useful to the caller? (If the caller wants to know whether foo is null, he can check it himself rather than calling our method and catching the NullPointerException!).

眼藏柔 2024-11-09 11:39:43

好的,这就是我发现的:

背景

基于以下原则,如 Bertrand Meyer 的书中所述
面向对象的软件构建

《无冗余原则》
情况应由机构
例行测试例行程序的
前提条件。”——伯特兰·迈耶

“先决条件可用性规则每
前提条件中出现的特征
例程必须可供
例行程序针对的每个客户
可用。”——伯特兰·迈耶

,这两点回答了这个问题:

  1. 为了使前提条件有用,客户端(方法的用户)必须能够测试它们。
  2. 服务器应该永远< /em> 测试前提条件,因为这会增加复杂性
    系统。尽管如此,在调试系统时会打开断言来执行此测试。

有关何时、为何以及如何使用先决条件的更多信息:

“合同设计的核心是
想法,表示为非冗余
原则,对于任何一致性
可能危及的情况
日常工作正常运转
应指定执行此
条件仅适用于两者之一
合同中的合作伙伴。哪一个?
在每种情况下你都有两个
可能性: • 您可以指定
对客户的责任,其中
如果条件将作为一部分出现
例程的前提条件。 • 或者
您指定供应商,其中
如果情况会出现在
if 形式的条件指令
条件 then ...,或同等条件
控制结构,在例程的
身体。

我们可以称之为第一态度
要求较高的和第二个
宽容。”——伯特兰·迈耶

因此,只有在确定客户承担责任时,才应存在先决条件。
由于服务器不应该测试前提条件,因此行为变得不确定(正如维基百科上所述)。

答案

  • 第一点回答了第一个例子。
  • 至于第二个例子,可能没有以正确的方式完成。这是因为
    第一个 @throws 声明暗示该方法具有(除了断言之外)
    测试了前提条件。这就违反了第二点。

至于空指针;这表明空指针责任已分配
到服务器。即用“宽容的态度”,而不是“苛求的态度”。
这完全没问题。如果一个人选择采取一种苛求的态度,那么他就会
删除 throws 声明(但更重要的是;对其进行测试),并添加
前提条件声明(也许还有断言)。

Ok, so this is what I've found out:

Background

Based on the following principles, as described in Bertrand Meyer's book
Object Oriented Software Construction:

"Non-Redundancy principle Under no
circumstances shall the body of a
routine ever test for the routine’s
precondition." - Bertrand Meyer

"Precondition Availability rule Every
feature appearing in the precondition
of a routine must be available to
every client to which the routine is
available." - Bertrand Meyer

, these two points answer this question:

  1. For preconditions to be useful, the client (user of method) has to be able to test them.
  2. The server should never test the precondition, because this will add complexity to the
    system. Although, assertions are turned on to do this testing when debugging the system.

More on when, why, and how to use preconditions:

"Central to Design by Contract is the
idea, expressed as the Non-Redundancy
principle, that for any consistency
condition that could jeopardize a
routine’s proper functioning you
should assign enforcement of this
condition to only one of the two
partners in the contract. Which one?
In each case you have two
possibilities: • Either you assign the
responsibility to clients, in which
case the condition will appear as part
of the routine’s precondition. • Or
you appoint the supplier, in which
case the condition will appear in a
conditional instruction of the form if
condition then ..., or an equivalent
control structure, in the routine’s
body.

We can call the first attitude
demanding and the second one
tolerant." - Bertrand Meyer

So a precondition should only exist if it is decided that the client holds the responsibility.
Since the server should not test the precondition, the behaviour becomes undefined (as also stated on Wikipedia).

Answers

  • The first point answers the first example.
  • As for the second example, it is probably not done the right way. This is because
    the first @throws declaration implies that the method has (other than an assertion)
    tested the precondition. This violates the second point.

As for the null pointer; this shows that the null pointer responsibility is assigned
to the server. That is, using a "tolerant attitude", as opposed to a "demanding attitude".
This is perfectly OK. If one chose to implement a demanding attitude, one would
remove the throws declaration (but more importantly; not test for it), and add
a precondition declaration (and perhaps an assertion).

紫瑟鸿黎 2024-11-09 11:39:43

我认为按契约思想设计(我自己还没有使用它)和前置/后置条件旨在保证从该方法传入和传出的特定条件。特别是编译器(在这种情况下,理论上因为 Java 没有内置的)需要能够验证合同条件。在您的文件前提条件的情况下,这是无法完成的,因为文件是外部资源,类可能会移动并且相同的文件可能不存在等等。编译器(或预处理器)如何保证这样的合同?

另一方面,如果您只是使用它进行注释,那么它至少会向其他开发人员显示您所期望的内容,但您仍然必须预料到当文件不退出时会出现异常。

我认为它“不适合”合同设计正式意义上的方法,因为它甚至无法针对一种情况进行验证。也就是说,您可以在一种环境中给出有效的文件名,但在程序外部的其他环境中可能无效。

另一方面,日期示例、前置条件和后置条件可以在调用者上下文中进行验证,因为它们不受方法本身无法控制的外部环境设置的影响。

I think that design by contract idea (I don't use it myself yet) and pre/post conditions are intended to guarantee specific conditions incoming and outgoing from the method. Particularly the compiler (in this case, theoretically as Java does not have this built-in) would need to be able to validate the contract conditions. In the case of your file precondition, this cannot be done since the file is an external resource, the class may move and the same file may not be there, etc. How can a compiler (or pre-processor) guarantee such a contract?

On the other hand, if you are just using it for comments then it would at least show the other developer what you expect but you must still expect too that there will be exceptions when the file does not exit.

I think that it "does not suit" the method in the formal sense of design by contract because it cannot be validated for even one case. That is, you may give valid file names in one environment but that may not be valid in other environment external to your program.

On the other hand, the date example, the pre and post conditions can be validated in the caller context as they are not influenced by external environmental setup that the method itself has no control over.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文