最近形成的 Manager 寫法 - Swift

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

會講到三個東西： **Manageable**, **Manager** 和 **ManagerControllable**

這篇用到寫法和命名皆採用 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` 這樣的字尾。

例如：

``` swift
protocol TokenManageable {
    
    /// 取用 token
    var token: String? { get }

    // ... 其他定義略
}
```

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

## 實作 - `~Manager`

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

### Singleton (單例)

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

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

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

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

``` swift
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 提供預設實作

先看這一段程式碼，

``` swift
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` ，也不用再撰寫其他實作就可以使用了：

``` swift
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 模組化的嘗試，但是還是有不滿意的地方（例如難以測試），這就是下一個目標了！