在前置条件中的块
本课程定义了契约编程,然后解释了契约编程中 in 块的工作原理。
合同编程
合同编程是一种软件设计方法,将软件的各个部分视为相互提供服务的独立实体。这种方法的核心思想是:只要服务的提供者和使用者都遵守约定,软件就能按照其规范正确运行。
D 语言的合同编程特性以函数作为软件服务的基本单元。与单元测试类似,合同编程也基于 assert 检查。
D 语言中的合同编程通过三种类型的代码块实现:
- 函数的
in块 - 函数的
out块 - 结构体和类的不变性块
in 块
函数的正确执行通常依赖于其参数值的有效性。例如,求平方根的函数要求参数不能为负数;处理日期的函数要求月份必须在 1 到 12 之间。这些对函数的要求被称为前置条件。
我们已经在 assert 和 enforce 章节 中见过类似的条件检查。可以通过在函数定义内部使用 assert 来强制执行参数值的条件:
1import std.stdio; 2import std.format; 3 4string timeToString(int hour, int minute) { 5 assert((hour >= 0) && (hour <= 23)); 6 assert((minute >= 0) && (minute <= 59)); 7 8 return format("%02s:%02s", hour, minute); 9} 10 11void main() { 12 writeln(timeToString(12, 34)); 13}
在合同编程中,同样的检查应写在函数的 in 块中。当使用 in 或 out 块时,函数的实际主体必须用 do 块来指定:
1import std.stdio; 2import std.string; 3 4string timeToString(int hour, int minute) 5in { 6 assert((hour >= 0) && (hour <= 23)); 7 assert((minute >= 0) && (minute <= 59)); 8} do { 9 return format("%02s:%02s", hour, minute); 10} 11 12void main() { 13 writeln(timeToString(12, 34)); 14}
注意: 在 D 语言早期版本中,此目的使用的是
body关键字,而非do。
使用 in 块的一个优点是,所有前置条件可以集中在一起,与函数的实际主体分离。这样,函数主体就不需要包含关于前置条件的 assert 检查。当然,根据需要,仍可在函数主体内部添加其他 assert 检查,用于防范函数内部可能出现的编程错误。
in 块中的代码会在每次调用函数时自动执行。只有当 in 块中的所有 assert 检查都通过后,函数才会真正执行。这可以防止使用无效的前置条件调用函数,从而避免产生错误结果。
若
in块中的assert检查失败,说明调用方违反了合同。
在下一课中,你将学习用于后置条件的 out 块。