Swift 中的合同设计
Design by Contract in Swift
Swift 是否提供原生的合同设计支持?我知道它可以在运行时通过断言完成,但是可以在编译时完成吗?或者,是否有任何外部 plugins/libraries 可以执行此操作?
编辑
我说 "during compile time Design by Contract",并不是说图书馆是 an all powerful static analyser that C# has。如果它像 iContract 为 Java 提供的那样,对我来说就足够了。让我们看一个例子:
使用 iContract 在 Java 中计算平方根的 DBC 代码可以写成:
/**
* @pre f >= 0.0
* @post Math.abs((return * return) - f) < 0.001
*/
public float sqrt(float f) { ... }
现在,这使我的合同成为我的 API 规范的一部分,而不是其实施的一部分,我认为这是一种更简洁的方式。呼叫者将知道他的职责是什么,而被呼叫者正在设定其期望,尽管所有这些都以更清晰的方式进行。我们在 Swift 有这样的东西吗?
我不知道这是否是您要找的东西,但这里有一个建议,您或许可以试试。
如果你想定义一个协议,你可以在其中定义 sqrt 函数的签名,并将实现留给其他 类 或结构在以后实现,你可以像下面的代码那样做:
注意:这里的sqrtf只是使用系统实现。
public protocol Math {
func sqrtf(f: Float) -> Float
}
struct NativeMath: Math {
func sqrtf(f: Float) -> Float {
return sqrt(f)
}
}
println(NativeMath().sqrtf(2))
TL;DR
正如@Tommy 在您的问题下的评论中指出的那样,您的问题的简单明了的答案似乎是“不,编译时 DbC 当前不是 Swift 的功能”。
现在内置了什么?
对于此类设计策略的内置支持,恐怕您目前必须查看运行时。 Swift 目前似乎更喜欢使用运行时断言来强制执行先决条件,尽管该语言似乎通常更加强调编译时的安全性(下面会详细介绍)。全局函数 assert
、assertionFailure
、precondition
和 preconditionFailure
旨在在不影响发布构建性能的情况下自由散布在整个代码中。
当然,单元测试是检查 API 合同是否得到履行的另一种策略,但这些必须手动考虑和实施,因此容易出错。
其他可能值得注意的是,在 Swift 2 更好的文档注释支持中,“requires”、“precondition”和“postcondition”是可识别的标记关键字,因此它们会显示在快速帮助文档中突出显示:
/// - precondition: f >= 0.0
/// - postcondition: abs((return * return) - f) < 0.001
/// - returns: The square root of `f`.
func sqrt(f: Float) -> Float { ... }
因此,强调能够为 API 合同提供良好的文档是否意味着 Swift 开发团队显然关心它,这是一个权宜之计,直到他们将某些东西纳入其中将来的语法,或者这是否意味着他们认为这种信息 属于 在文档中?毫无意义的假设,也许。无论如何,尽管它不是正确的 DbC,但我认为现在注意它是一件很方便的事情。
我现在可以做什么?
使用 Objective-C、macros could be used 从本质上实现基本的 DbC,但是 Swift 中缺少宏意味着您将不得不求助于某种 function/generics-based 包装器,我认为这看起来真的很尴尬。
Xcode 支持将自定义脚本添加到目标的构建阶段——正如@JonShier 在评论中所建议的那样——这可能是你最接近有用和自动 DbC 的方法,而无需等待语言(也许/也许不)引入这样的功能。使用上述文档标记关键字,分析文档注释以构建单元测试的脚本甚至可以追溯合并到用户只有少量 learning/effort 的项目中。正如你所说,我认为这可以成为一个非常有趣的项目!
将来会成为内置功能吗?
目前尚不清楚原生 DbC 将来是否会被纳入 Swift。可以说,它是一个非常适合 Swift 语言使命的功能,也就是说它促进了更安全的代码并降低了运行时错误的风险。如果它成为语言的一部分,我建议我们更有可能看到它们显示为 declaration attributes 而不是解释注释标记,例如:
@contract(
precondition = f >= 0.0,
postcondition = abs((return * return) - f) < 0.001
)
func sqrt(f: Float) -> Float { ... }
(但这只是猜测,目前对我们没有任何用处!)
据我所知,编译时 DbC 可能是一个非常 复杂的问题。但谁知道呢……在 Clang 静态分析器上的工作肯定表明存在将运行时错误的识别拖回编译时的潜在愿望。也许这是在将来使用 Swift 静态分析器的完美问题?
Swift 是否提供原生的合同设计支持?我知道它可以在运行时通过断言完成,但是可以在编译时完成吗?或者,是否有任何外部 plugins/libraries 可以执行此操作?
编辑
我说 "during compile time Design by Contract",并不是说图书馆是 an all powerful static analyser that C# has。如果它像 iContract 为 Java 提供的那样,对我来说就足够了。让我们看一个例子:
使用 iContract 在 Java 中计算平方根的 DBC 代码可以写成:
/**
* @pre f >= 0.0
* @post Math.abs((return * return) - f) < 0.001
*/
public float sqrt(float f) { ... }
现在,这使我的合同成为我的 API 规范的一部分,而不是其实施的一部分,我认为这是一种更简洁的方式。呼叫者将知道他的职责是什么,而被呼叫者正在设定其期望,尽管所有这些都以更清晰的方式进行。我们在 Swift 有这样的东西吗?
我不知道这是否是您要找的东西,但这里有一个建议,您或许可以试试。 如果你想定义一个协议,你可以在其中定义 sqrt 函数的签名,并将实现留给其他 类 或结构在以后实现,你可以像下面的代码那样做:
注意:这里的sqrtf只是使用系统实现。
public protocol Math {
func sqrtf(f: Float) -> Float
}
struct NativeMath: Math {
func sqrtf(f: Float) -> Float {
return sqrt(f)
}
}
println(NativeMath().sqrtf(2))
TL;DR
正如@Tommy 在您的问题下的评论中指出的那样,您的问题的简单明了的答案似乎是“不,编译时 DbC 当前不是 Swift 的功能”。
现在内置了什么?
对于此类设计策略的内置支持,恐怕您目前必须查看运行时。 Swift 目前似乎更喜欢使用运行时断言来强制执行先决条件,尽管该语言似乎通常更加强调编译时的安全性(下面会详细介绍)。全局函数 assert
、assertionFailure
、precondition
和 preconditionFailure
旨在在不影响发布构建性能的情况下自由散布在整个代码中。
当然,单元测试是检查 API 合同是否得到履行的另一种策略,但这些必须手动考虑和实施,因此容易出错。
其他可能值得注意的是,在 Swift 2 更好的文档注释支持中,“requires”、“precondition”和“postcondition”是可识别的标记关键字,因此它们会显示在快速帮助文档中突出显示:
/// - precondition: f >= 0.0
/// - postcondition: abs((return * return) - f) < 0.001
/// - returns: The square root of `f`.
func sqrt(f: Float) -> Float { ... }
因此,强调能够为 API 合同提供良好的文档是否意味着 Swift 开发团队显然关心它,这是一个权宜之计,直到他们将某些东西纳入其中将来的语法,或者这是否意味着他们认为这种信息 属于 在文档中?毫无意义的假设,也许。无论如何,尽管它不是正确的 DbC,但我认为现在注意它是一件很方便的事情。
我现在可以做什么?
使用 Objective-C、macros could be used 从本质上实现基本的 DbC,但是 Swift 中缺少宏意味着您将不得不求助于某种 function/generics-based 包装器,我认为这看起来真的很尴尬。
Xcode 支持将自定义脚本添加到目标的构建阶段——正如@JonShier 在评论中所建议的那样——这可能是你最接近有用和自动 DbC 的方法,而无需等待语言(也许/也许不)引入这样的功能。使用上述文档标记关键字,分析文档注释以构建单元测试的脚本甚至可以追溯合并到用户只有少量 learning/effort 的项目中。正如你所说,我认为这可以成为一个非常有趣的项目!
将来会成为内置功能吗?
目前尚不清楚原生 DbC 将来是否会被纳入 Swift。可以说,它是一个非常适合 Swift 语言使命的功能,也就是说它促进了更安全的代码并降低了运行时错误的风险。如果它成为语言的一部分,我建议我们更有可能看到它们显示为 declaration attributes 而不是解释注释标记,例如:
@contract(
precondition = f >= 0.0,
postcondition = abs((return * return) - f) < 0.001
)
func sqrt(f: Float) -> Float { ... }
(但这只是猜测,目前对我们没有任何用处!)
据我所知,编译时 DbC 可能是一个非常 复杂的问题。但谁知道呢……在 Clang 静态分析器上的工作肯定表明存在将运行时错误的识别拖回编译时的潜在愿望。也许这是在将来使用 Swift 静态分析器的完美问题?