iOS 遵循開閉原則的實際案例討論

黑羽肅霜_發表於2019-04-11

案例:

假設現在有一個工具類,目的是把傳入頁面指定區域渲染成紅色

不好的設計

定義一個基類 BaseFlushedViewController: UIViewController,返回一個 flushArea, 供工具類進行染色。

class FlushHelper {
    static func flush(_ viewController: BaseFlushedViewController) {
        viewController.flushArea().backgroundColor = .red
    }
}

class BaseFlushedViewController: UIViewController {
    func flushArea() -> UIView {
        return view
    }
}
複製程式碼

那麼我們傳入的引數必須是他的子類,而且由於每個頁面可能需要重新整理的檢視是不同的,我們理論上應該重寫他的 flushArea 方法

這樣做的問題有兩個:

  • 新建的頁面類可能會忘記重寫該方法
  • 如果需要傳入的頁面是一個 UINavigatiionControllerUITabbarController呢?(有可能我現在要渲染導航欄或底部標籤欄),那麼我還要再在工具類中新增兩個介面適應。這顯然不是我們想要的
class FlushHelper {
    static func flush(_ viewController: BaseFlushedViewController) {
        viewController.flushArea().backgroundColor = .red
    }
    
    static func flush(_ viewController: BaseFlushedNavViewController) {
        viewController.flushArea().backgroundColor = .red
    }
    
    static func flush(_ viewController: BaseFlushedTabViewController) {
        viewController.flushArea().backgroundColor = .red
    }
}

class BaseFlushedViewController: UIViewController {
    func flushArea() -> UIView {
        return view
    }
}

class BaseFlushedNavViewController: UINavigationController {
    func flushArea() -> UIView {
        return view
    }
}

class BaseFlushedTabViewController: UITabBarController {
    func flushArea() -> UIView {
        return tabBar
    }
}
複製程式碼

面相介面的設計

定義一個協議

protocol Flushable {
    func flushArea() -> UIView
}

class FlushHelper {
    static func flush(_ viewController: UIViewController & Flushable) {
        viewController.flushArea().backgroundColor = .red
    }
}

class SomeViewController: UIViewController, Flushable {
    func flushArea() -> UIView {
        return view
    }
}

class SomeNavViewController: UINavigationController, Flushable {
    func flushArea() -> UIView {
        return navigationBar
    }
}

class SomeFlushedTabViewController: UITabBarController, Flushable {
    func flushArea() -> UIView {
        return tabBar
    }
}
複製程式碼

將工具類的介面統一成 UIViewController & Flushable

這樣做的好處:

  • 呼叫工具類介面時,十分明確的知道傳入的頁面要去實現 flushArea 方法,不存上文提到的在繼承之後忘記重寫的情況
  • 適配所有的 UIViewController 及其子類。不需要工具類再開放額外的介面

比較

看起來面向介面的方法和使用基類的方法相比,只是工具類中的介面統一成了一個。但實際上,面向介面的方法中,SomeNavViewControllerSomeFlushedTabViewController 都是 UIViewController 的一種特例。而使用基類的實現方法中,三種 Base 類則是平級的基類,是為了覆蓋所有的頁面型別。

假設如果有一種新的導航頁面,那麼面向基類的方法,就還要去新增一個新的基類覆蓋這個特例,面向介面的則只需要擴充套件出一個新的型別即可,工具類介面不需要新增。

以上的思想,就是物件導向設計規則中 開閉原則的一種體現(若有錯誤歡迎指正)。

相關文章