Swift合同设计

时间:2015-08-04 19:11:06

标签: ios swift design-by-contract

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

修改

在编译时按照合同设计"说"我并不是说库是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中有这样的东西吗?

2 个答案:

答案 0 :(得分:11)

TL; DR

正如@Tommy在你提问的评论中指出的那样,看起来普通&amp;对你的问题的简单回答是“不,编译时DbC目前不是Swift的一个功能”。

现在有什么内容?

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

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

其他值得注意的是,在Swift 2的更好的文档评论支持中,“要求”,“前置条件”和“后置条件”是可识别的标记关键字,因此它们在快速帮助文档中显着显示:

/// - 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中缺少宏意味着你必须求助于某种基于函数/泛型的包装器,我认为它看起来像一个真的很尴尬。

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


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

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

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

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

据我所知,编译时DbC可能是非常复杂的问题。但是谁知道...... Clang Static Analyzer的工作肯定表明,有一个潜在的愿望是将运行时错误的识别拖回编译时间。也许这是将Swift静态分析仪用于未来的完美问题?

答案 1 :(得分:-4)

我不是,如果这是你正在寻找的东西,但这里有一个你可以试试的建议。 如果要定义一个协议,您可以在其中定义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))