《重構與模式》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

相關文章
相關標籤/搜索