异常是有效的后置条件吗?
Is an exception a valid postcondition?
考虑以下界面:
public interface AISPI
{
public Path getPath(Entity entity, Entity target, World world) throws NoPathException;
}
假设 entity、target 和 world 都是有效输入。但是用于查找路径的算法(在这种情况下是 Astar)无法找到路径,例如。 目标的位置被混凝土墙包围。
声明后置条件是从 entity 到 target[=28= 的 Path 是否有效](开始到目标)或 NoPathException(假定未找到路径)?
- 或 前提条件是否应该声明从开始到目标必须有一条有效路径?
这不是作业,而是为了完善我们学期项目报告的一道题。我不想学习任何框架,这纯粹是关于合同设计的标准和手续问题。感谢您对此事的任何澄清。
这取决于术语后置条件的定义。一般来说,前置条件是程序进入时输入状态和输入值的关系,后置条件是程序退出时输入状态、输入值和输出状态及输出值的关系。
因为例程可以正常退出或异常退出,所以可以定义正常终止的后置条件和异常终止的后置条件。显然两者都涉及输入值、输入状态和输出状态。关键区别在于输出值。在第一种情况下,这是在例程签名中指定的值,在第二种情况下 - 它取决于语言。在您的示例中,它可能是 NoPathException
,但如果存在内存分配错误、堆栈溢出或签名中未指定的其他异常或信号怎么办?似乎确实有可能有一个前提条件来保证总是有一个不涉及异常的有效结果。但事实并非如此,例如当存在与外部世界的通信、并发性等时。此外,如果先决条件的计算成本太高,那么两次执行相同的工作看起来并不好——在客户端确保调用适用,在供应商方面进行基本相同的计算以获得结果。
根据按合同设计 哲学,后置条件是客户在调用例程后可以安全依赖的条件。回到异常情况,从实际的角度来看,使异常后置条件足够强以便程序可以继续执行但足够弱以便签名中没有或不能指定的情况是有意义的,但是在实践中是可能的,是允许的。
因此,除非该语言确实保证所有可能的异常情况,否则最重要的部分是不应使关联对象不可用的输出状态。这可以用显式或隐式后置条件或 class 不变量来表示。
关于getPath
的具体例子,路径不存在的情况是正常的,即可能发生,这是意料之中的。一些准则建议使用正常值来指示正常终止情况。在这里它将是值 null
。使用 null
可能会导致调用方出现其他问题,例如 NullPointerException
如果未检查结果是否为空,但在某些保证不存在此类异常的语言中(例如,void-safety in Eiffel) 这将是指示缺少路径的首选方式(在这种情况下 return 类型将是 detachable PATH
)。
考虑以下界面:
public interface AISPI
{
public Path getPath(Entity entity, Entity target, World world) throws NoPathException;
}
假设 entity、target 和 world 都是有效输入。但是用于查找路径的算法(在这种情况下是 Astar)无法找到路径,例如。 目标的位置被混凝土墙包围。
声明后置条件是从 entity 到 target[=28= 的 Path 是否有效](开始到目标)或 NoPathException(假定未找到路径)?
- 或 前提条件是否应该声明从开始到目标必须有一条有效路径?
这不是作业,而是为了完善我们学期项目报告的一道题。我不想学习任何框架,这纯粹是关于合同设计的标准和手续问题。感谢您对此事的任何澄清。
这取决于术语后置条件的定义。一般来说,前置条件是程序进入时输入状态和输入值的关系,后置条件是程序退出时输入状态、输入值和输出状态及输出值的关系。
因为例程可以正常退出或异常退出,所以可以定义正常终止的后置条件和异常终止的后置条件。显然两者都涉及输入值、输入状态和输出状态。关键区别在于输出值。在第一种情况下,这是在例程签名中指定的值,在第二种情况下 - 它取决于语言。在您的示例中,它可能是 NoPathException
,但如果存在内存分配错误、堆栈溢出或签名中未指定的其他异常或信号怎么办?似乎确实有可能有一个前提条件来保证总是有一个不涉及异常的有效结果。但事实并非如此,例如当存在与外部世界的通信、并发性等时。此外,如果先决条件的计算成本太高,那么两次执行相同的工作看起来并不好——在客户端确保调用适用,在供应商方面进行基本相同的计算以获得结果。
根据按合同设计 哲学,后置条件是客户在调用例程后可以安全依赖的条件。回到异常情况,从实际的角度来看,使异常后置条件足够强以便程序可以继续执行但足够弱以便签名中没有或不能指定的情况是有意义的,但是在实践中是可能的,是允许的。
因此,除非该语言确实保证所有可能的异常情况,否则最重要的部分是不应使关联对象不可用的输出状态。这可以用显式或隐式后置条件或 class 不变量来表示。
关于getPath
的具体例子,路径不存在的情况是正常的,即可能发生,这是意料之中的。一些准则建议使用正常值来指示正常终止情况。在这里它将是值 null
。使用 null
可能会导致调用方出现其他问题,例如 NullPointerException
如果未检查结果是否为空,但在某些保证不存在此类异常的语言中(例如,void-safety in Eiffel) 这将是指示缺少路径的首选方式(在这种情况下 return 类型将是 detachable PATH
)。