《重构与模式》Swift 版之参数对象

做者:Natasha The Robot,原文连接,原文日期:2016-05-28
译者:Channe;校对:Cee;定稿:千叶知风git

我最近在读《重构与模式》 。昨天(译注:原文日期的昨天),在我写描述了一个拥有多个参数的对象的《建立方法》时,想到了@modocache关于iOS API 设计中的 Swift 模式超棒的演讲,尤为是关于参数对象部分。我第一次看的时候获益匪浅,所以我但愿记录下来。github

问题

假设你在写一个 BananaUIKit 库,包含了一个简单的 BananaAlertView:swift

最开始的代码可能想这样:设计模式

public class BananaAlertView {
    
    public static func show(
        withTitle title: String,
        message: String,
        dismissButtonText: String)
    {
        // 具体实现
    }
}

这个实现很好,直到一位使用这个框架的用户请求可以将 BananaAlertView 的颜色由棕色换为黄色...闭包

为了确保更改这个框架不影响其余用户,咱们使用 Swift 的默认参数:框架

public class BananaAlertView {
    
    public static func show(
        withTitle title: String,
        message: String,
        dismissButtonText: String,
        // 新的无痛更改
        tintColor: UIColor = .yellowColor())
    {
        // 具体实现
    }
}

只要咱们给方法添加参数,它就能很好的工做。可是若是咱们想要将参数添加到别的东西就不行了,好比 BananaAlertView 上一个按钮被点击后的闭包:spa

public class BananaAlertView {
    
    // 按钮被点击后的动做
    public typealias ButtonCallback = (buttonIndex: Int) -> Void
    
    public static func show(
        withTitle title: String,
        message: String,
        dismissButtonText: String,
        // 回调参数
        dismissButtonCallback: ButtonCallback)
    {
        // 具体实现
    }
}

// 用法

BananaAlertView.show(
    withTitle: "This is Bananas",
    message: "Someone has been monkeying around ?",
    dismissButtonText: "Banana",
    dismissButtonCallback: { buttonIndex in
        // 具体实现
    })

可是假如咱们须要改变闭包的参数呢?假如客户端一样须要按钮的文本呢?翻译

解决方式就是为 ButtonCallback 添加一个按钮文本的参数:设计

public typealias ButtonCallback = (buttonIndex: Int, buttonTitle: String) -> Void

可是这破坏了一切...当调用 show 方法时,ButtonCallback 方法此时须要两个参数,而不是原来的一个了。code

// 用法

BananaAlertView.show(
    withTitle: "This is Bananas",
    message: "Someone has been monkeying around ?",
    dismissButtonText: "Banana", 
    // 破坏了原来的调用
    // 闭包须要带有两个参数:buttonIndex 和 buttonText
    dismissButtonCallback: { buttonIndex in
        // 具体实现
    })

因而咱们该怎么办?此时参数对象就该上场了!

解决方案

解决方案是为闭包建立一个参数对象:

public class BananaAlertView {
    
    // 参数对象
    public struct ButtonCallbackParameters {
        let buttonIndex: Int
        let buttonTitle: String
    }
    
    // 如今只须要一个参数
    public typealias ButtonCallback = (parameters: ButtonCallbackParameters) -> Void
    
    public static func show(
        withTitle title: String,
        message: String,
        dismissButtonText: String,
        dismissButtonCallback: ButtonCallback)
    {
        // 具体实现
    }
}

BananaAlertView.show(
    withTitle: "This is Bananas",
    message: "Someone has been monkeying around ?",
    dismissButtonText: "Banana",
    // 参数对象包含全部调用者须要的参数
    dismissButtonCallback: { parameters in
        if parameters.buttonTitle == "Banana" {
            // 具体处理代码
        }
    })

如今须要添加额外的参数时,代码依旧能工做得很是好。buttonCallback 彻底不须要变更。

public struct ButtonCallbackParameters {
        let buttonIndex: Int
        let buttonTitle: String
        // 新参数
        let buttonCount: Int
}

固然,你也能够轻松删除或移除参数:

public struct ButtonCallbackParameters {
        let buttonIndex: Int
        // 下个版本中移除 buttonTitle
        @available(*, deprecated=2.0)
        let buttonTitle: String
        let buttonCount: Int
}

其余用法

固然,能够重构方法时用更通用的方式来得到愈来愈多的参数:

public class BananaAlertView {
    
    // 显示警告视图时,视图选项不是必须的
    // 这里可以提供默认值
    public struct AlertViewOptions {
        public let dismissButtonText = "Bananana"
        public let tintColor = UIColor.yellowColor()
        public let animationDuration: NSTimeInterval = 1
    }
    
    public static func show(
        withTitle title: String,
        message: String,
        options: AlertViewOptions)
    {
        // 具体实现
    }
}

权衡

和其余设计模式同样,学会和用好彻底是两件事,知道它们是好事,可是在使用前须要权衡一下。找到最佳的平衡点是咱们码农的工做,须要不断努力才能找到最佳实践?。

对于参数对象来讲,好处是可以为将来预留 API,可是这样的预留是有负担的。你不能为此给每一个方法和闭包都新建一个新结构体。

因此,请明智的使用!

最后,我强烈推荐观看 @modocache 演讲的完整视频。

本文由 SwiftGG 翻译组翻译,已经得到做者翻译受权,最新文章请访问 http://swift.gg

相关文章
相关标签/搜索