繁体   English   中英

Swift合同设计

[英]Design by Contract in Swift

Swift是否提供本地设计合同支持? 我知道它可以在运行时通过断言完成,但它可以在编译期间完成吗? 或者,有没有外部插件/库这样做?

编辑

通过说“在编译期间按合同设计”,我并不是说库是C#所有功能强大的静态分析器 如果它像iContract为Java提供的那样就足够了。 我们来看一个例子:

使用iContract在Java中进行平方根评估的DBC代码可以写成:

/** 
 * @pre f >= 0.0
 * @post Math.abs((return * return) - f) < 0.001 
 */ 
public float sqrt(float f) { ... } 

现在,这使我的合同成为我的API规范的一部分而不是其实现的一部分,我认为这是一种更清洁的方式。 呼叫者将知道他的职责是什么,被呼叫者正在设定其期望,所有这些都是更清晰的方式。 我们在Swift中有这样的东西吗?

TL; DR

正如@Tommy在你的问题的评论中指出的那样,对你的问题的简单回答似乎是“不,编译时DbC目前不是Swift的一个特性”。


现在有什么内置的?

对于这种类型的设计策略的内置支持,您目前必须查看我担心的运行时。 Swift似乎更喜欢运行时断言来强制执行前提条件,尽管语言似乎通常在编译时更加强调安全性(更多内容见下文)。 全局函数assertassertionFailurepreconditionpreconditionFailure旨在在整个代码中自由地散布,而不会影响发布版本的性能。

当然,单元测试是检查API合同是否已满足的另一种策略,但这些必须手动考虑并实施,因此容易出错。

还有一点值得注意的是,在Swift 2的更好的文档评论支持中,“require”,“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, 宏可用于实质上实现基本的DbC,但是Swift中缺少宏意味着你不得不求助于某种基于函数/泛型的包装器,我认为它看起来像是一个非常尴尬的躲避。

Xcode支持将自定义脚本添加到目标的构建阶段 - 正如@JonShier在评论中所建议的那样 - 可能是最接近有用和自动DbC的,而无需等待语言(可能/可能不是)引入这样的功能。 使用上述文档标记关键字,分析文档注释以构建单元测试的脚本甚至可以追溯地包含在用户只需要少量学习/努力的项目中。 正如你所说,我认为这可以成为一个非常有趣的项目!


它将来会成为内置功能吗?

目前尚不清楚未来是否可能将原生DbC纳入Swift。 可以说,它是一个非常适合Swift语言任务的功能,也就是说它可以促进更安全的代码并降低运行时错误的风险。 如果它成为语言的一部分,我建议我们更有可能将它们看作声明属性而不是解释的注释标记,例如:

@contract(
    precondition = f >= 0.0,
    postcondition = abs((return * return) - f) < 0.001
)
func sqrt(f: Float) -> Float { ... } 

(但这只是猜测,现在对我们毫无用处!)

据我所知,编译时DbC可能是一个非常复杂的问题。 但是谁知道...... Clang Static Analyzer的工作肯定表明,有一个潜在的愿望是将运行时错误的识别拖回编译时间。 也许这是将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))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM