LoginSignup
2
2

More than 5 years have passed since last update.

最近形成的 Manager 寫法 - Swift

Posted at

在撰寫 app 的時候,會有一些 manager 來處理商業邏輯。而這些 managers 本身會被其他的 class 或是 struct 的實體 (instance) 用到。這時候考量到 可測試易抽換 ,就會使用「相依性注入」 (DI, Dependency Injection) 這個手法,把這個 manager 的實體,可以讓其他類別的實體使用。

會講到三個東西: Manageable, ManagerManagerControllable

這篇用到寫法和命名皆採用 Swift 3 / iOS 10 SDK 的命名慣例,並以「Token 管理」作為輔助說明的範例。

管理者類型介面 - ~Manageable

針對介面,而非針對實作來撰寫程式碼

這句話應該很熟悉,原文是「program to an interface not an implementation」。

說到介面,在 Swift 就是 protocol ,protocol 可以讓我們拿來定義和整理預期行為的介面。

命名

在 Swift protocol 命名的慣例上,只要這個 protocol 的目的,是跟 可以 達成什麼事情有關係,字尾就會是 ~able

因為在這裡是期望實作這一類型 protocols 的 manager s 是要 可以 管理某個東西,所以在 manager 類型的 protocol 的命名,複合之後,就都會有 ~Manageable 這樣的字尾。

例如:

protocol TokenManageable {

    /// 取用 token
    var token: String? { get }

    // ... 其他定義略
}

定義了一個 TokenManageable 這個 protocol ,並具有取用 token 的功能。

實作 - ~Manager

這個部分就很簡單,實作的 class 就會以 ~Manager 作為字尾。

Singleton (單例)

就像 iOS 原生的 NotificationCenter 一樣,我對 manager 類型的實作

  1. 使用 class
  2. 以 singleton 的形式存在

一方面也是有考量到測試的可 mock 性。

所以在實作的時候,通常會加上類似這樣的實作:

class TokenManager: TokenManageable {

    // 主要的 type variable ,是個 singleton 物件
    static let `default`: TokenManager = TokenManager()

    // 如果期望這個 manager 不能有第二的實體,就必須把 init method 設定成 private
    private init() {
    }

    /// MARK: - TokenManageable

    var token: String? {
        // 實務上可能由某處取出這個 token
        return ""
    }

    // ... 其他實作略
}

可使用 - ~ManagerControllable

這個設計想解決的問題:

原本重構到使用 DI 的方式提供共用的 managers ,
但是這時候發現每個使用的 class/struct 都要實作的話,會有很多重複程式碼。
因此決定再封裝這一個部分,
同時給這個賦予 更容易識別 的名稱

===

當建立好 manager 的時候,就可以給其他實體使用,概念上是:

可控制某某 manager

先挑了很多英文單字當候選之後,
覺得 Control 比較符合我期望他的目的,
因此字尾最終以 ~ManagerControllable 的樣子呈現。

定義與實作

這一段有用到這些概念

  • Dependency Injection (相依性注入)
  • 為 Protocol 提供預設實作

先看這一段程式碼,

protocol TokenManagerControllable {
    var tokenManager: TokenManageable { get }
}

extension TokenManagerControllable {
    var tokenManager: TokenManageable {
        return TokenManager.default
    }
}

首先,TokenManagerControllable 定義可以取用 tokenManager 的 protocol 。

接著利用「 extension 可為 protocol 提供預設實作 」的特性,
讓這個 protocol 在不需要另外實作的情形之下,
只要某個類別使用 (conforms) 這個 protocol ,
他的實體只要呼叫 tokenManager 就可以取用到上一個段落時做出來的 singleton 物件 - TokenManager.default

實際使用

假設有某個 UIViewController 子類別 ViewController 想要使用 token manager 的話,只要像這樣 conforming TokenManagerControllable ,也不用再撰寫其他實作就可以使用了:

class ViewController: UIViewController, TokenManagerControllable {

    override func viewDidLoad() {
        super.viewDidLoad()

        if let token = self.tokenManager.token {
            print("Token 存在: \(token)")
        } else {
            print("Token 不存在")
        }
    }

    // ... 其他實作略
}

結語

這一陣子在上班時間和下班的零碎時間,都花很多時間在思考這一類的東西

  • Protocol oriented programming / 介面導向設計
  • Clear and easy-understand behaviors / 清晰易懂的介面行為
  • Testable / 可測試
  • Embracing changing / 擁抱改變
  • Easy maintainable / 易可維護

也趁著 Swift 3 改版和新專案的契機,把商業邏輯 manager 這個部分做得更完善;並把概念定下來,幫助在專案上能夠更迅速的實作和測試,語意上同時也更加精確表達他們想達成的目的。

在實務上,熟練後確實也幫助我更快更精確地加速撰寫單元測試案例、在商業需求上要變更需求的時候減少了非常多的替換成本。

這一段時間在商業邏輯層架構花的功非常多,下一個階段應該就是 UI 相關實作的調整了吧。雖然之前有做過 UI 模組化的嘗試,但是還是有不滿意的地方(例如難以測試),這就是下一個目標了!

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2