首页 文章

Swift中的 Contract 式设计

提问于
浏览
17

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

EDIT

通过说"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) { ... }

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

2 回答

  • -4

    TL; DR

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


    现在有什么内置的?

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

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

    还有一点值得注意的是,在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 Contract 提供良好的文档意味着Swift开发团队明确关注它,这是一个止步距离,直到他们在未来将某些内容融入语法中,或者它是否意味着他们的思考这类信息属于文档?也许是毫无意义的假设 . 无论如何,尽管它现在是一个值得注意的事情 .

    我现在该怎么办?

    使用Objective-C,macros could be used基本上实现了基本的DbC,但是Swift中缺少宏意味着你不得不求助于某种基于函数/泛型的包装器,我认为这看起来像是一个非常尴尬的躲避 .

    Xcode支持将自定义脚本添加到目标的构建阶段 - 正如@JonShier在评论中所建议的那样 - 可能是最接近有用和自动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静态分析仪放在未来的完美问题?

  • 11

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

相关问题