💭

マーティン・ファウラー氏のPassive Viewパターンを読み解いてSwiftで例を表現したい

2022/08/10に公開約4,400字

はじめに

ここ数年のiOSアプリ開発でよく使われているMVPのPassive Viewパターンについて、原著のマーティン・ファウラー氏のブログ記事を振り返って読んで、念の為理解を深めてみようと思って書いてみました。

ただの感想文ですが、
マーティン氏の記事の例をSwiftで表現してみたら少しは分かりやすいかもしれないと思って載せています。

もし何か認識違いがあればコメントで指摘いただければ幸いです。

最初に私の結論

  • Passive ViewはMVP特有のものではない
    • MVCでも使える
      • マーティン氏の説明でもControllerが使われており、そこはPresenterと読み替えても良さそう
  • Passive ViewはViewが受動的(Passive)で能動的ではない
    • あくまでViewは受け身でありPresenterがViewに値をセットしたりする
      • 自分で値をセットしたりしない
  • ViewはUIイベントをPresenterに伝える
    • Presenterにイベントを送信する
      • ViewがPresenterのメソッドを呼び出す

あと、念の為

  • Passive Viewの重要でないこと
    • PresenterがViewをObserveするかどうかはあまり重要ではない
      • 説明文には述べられていないが例ではPresenterがViewをObserveしている
        • 説明にはないのでそこは必須じゃなくて良さそう
    • Swiftプログラミングだと当たり前だがそうでないこと
      • weakだとかOptionalだとかはあくまでSwiftの都合であって原著ではそこらへんは記載していない

本題: マーティン・ファウラーのPassive View

https://martinfowler.com/eaaDev/PassiveScreen.html

箇条書きで雑にまとめていきます

序文っぽいもの

  • リッチクライアントシステム(クライアント環境で動くアプリ、WindowsアプリとかiOSアプリとか)はテストが複雑
    • なぜなら
      • フレームワークがテストを念頭に置いて構築されていないから
  • Passive View
    • やること
      • ユーザイベントへの応答を処理する
      • Viewの更新を行うためのコントローラを使用する
        • (訳注: MVCのときはコントローラを利用するという意味でMVPのときはPresenterがやることでしょう)
        • View(UIコンポーネント)の動作を最小限に抑える

> How It Works

  • Passive ViewというのはMVCやMVPを使う場合のバリエーション
    • (訳注: MVPでPassive Viewを使ったり、MVCでPassive Viewを使うということでしょう)
    • UIというものは2つに分割される
      • 2つ
        • 表示を処理するビュー
        • ユーザイベント(ジェスチャーなど)に応答するコントローラ
  • 特徴
    • Viewは完全に受け身(Passive)
    • Modelから自分自身を更新する責任がない
      • Viewのロジックはコントローラの中にある
      • ViewとModel間には依存関係がない
  • assessmentウィンドウの例
    • (訳注: assessmentウィンドウというViewがあると思うのがよろしいかと)
    • ユーザ操作をすぐにコントローラに伝える
      • コントローラはModelを更新する
        • Modelからビューを再読込する
        • ウィジェット(訳注: Viewのことをウィジェットって表現したりUIコンポーネントって言ったりしてる?)を更新
  • 目的
    • 主に自動テスト
      • コントローラのテストでは
        • Viewのテストダブル(スタブ/モック)を利用し、UIフレームワークとのインタラクションを必要としない
      • 中間ゲートウェイをセットアップする
        • (訳注: それは詳細をもっと示してくれ)

マーティン氏のブログからシーケンス図を引用

シーケンスを雑に日本語で順に表現すると次のようになる感じでしょうか

  • TextFieldに50が入力される
  • textが変更されるとControllerが検知
  • Controllerがtextを取得
  • 50がModel側に渡される
  • ControllerはloadView
    • Model(:Reading)から50取得
    • viewへ50をset
    • Modelからvarianceを取得(varianceってのは50の場合に変わる値?)
    • varianceなtextFieldにvarianceの値をset
    • ModelからvarianceなCategoryを取得
    • varianceなtextFieldへCategoryを解釈しredをセットする

上記シーケンス図から簡易的にSwiftで表現してみます。

class AssesmentWindow {
    class Field {
        var text: Text // getter / setter
    }
    
    var actualField: Field
    var varianceField: Field
}

class AssesmentController {   
    private let view: AssesMentWindow
    private let record: AssesmentRecord
    
    func observe() {
        // viewの値がかわったらloadViewが動くようにする
        observe(view) { $0
	    record.actual = $0
	    loadView()
	}
    }

    private func loadView() {
        // get actual
        let value = record.actual        
        // set text
        view.actualField.text.value = value
        // get variance
        let variance = record.variance        
        // set text
        view.varianceField.text.value = variance
        // get variance category
        let category = record.varianceCategory        
        // set text color
        // 以下プレゼンテーションロジック部分
        let color = ...
        switch category {
            case ...: //
            color = .red // categoryからredを
            ...
        }
        view.varianceField.text.color = color 
    }
}

class AssesmentRecord {
    var actual: Int
    private(set) var variance: Int
    private(set) var varinaceCategory: Category
}

> When to Use It

  • Passive Viewの理由
    • Viewをコントローラの矮小された奴隷にすることでViewをテストしないことによるリスクはほとんどなくなる
      • (訳注: the view reduced to a dumb slave of the controller, の表現わかりづらすぎ。深く考えたくない)
    • テストにおいてPassive Viewが一番優れてるわけじゃない

Passive View記事自体の感想

記事の中ではView、UIコンポーネント、ウィジェットなどの用語がぶれているのかあえて別々な言い方をしたいのかが正直私にはわからないなというのが読むたびに思いますね。

表現として、the view reduced to a dumb slave of the controller など表現が訳しづらいがニュアンスは分かるようなのがあります。言いたいことは分かるが、すごく日本語として汚くないように訳すことは難しそうですね。

さらに中間ゲートウェイをつかったテスト手法部分もあるんですが、それについてはあまり深追いしても意味が無さそうなんで除外しています。

Discussion

ログインするとコメントできます