デザインパターンとリファクタリング

この記事はEnigmo Advent Calendar 2018の16日目です

デザインパターンリファクタリング

こんにちは。iOSチームでエンジニアをやっています

今チームでは、プロジェクトの進行と並行してリファクタリングを行なっています

対象プロジェクトは、MVVMのデザインパターンを多用しています

そのプロジェクトをどうリファクタリングしていったかをツラツラと書いていこうかと思います

TL;DR

  • デザインパターンって、設計パターン。うまく対処するためにどう設計していけばいいかをまとめたものです
  • デザインパターンを各画面で分けよう
  • 簡単な実装なのに、MVVMを利用したら複雑になってしまった、であれば、設計を間違えている

リファクタリング

リファクタリングについては、短期間で見ると、ビジネスとしてぶっちゃけ一円にもなりません。 しかし、リファクタリングをする、しないでは、未来のプロジェクトの進行速度に影響していきます。

なぜリファクタリングをしたのか

  • メンテ、新規開発がしづらい
  • 数行いじると全然違う画面でエラーがでてしまう
  • どの画面がどのViewControllerなのかわからない
  • Swiftらしいコードに
  • MVVMなのに、UIViewControllerがふとっている

着手前

[アプリ設計]

  • RxSwift / RxSwift Community の様々なライブラリを使って MVVM を 試してみた実装
  • iOS5の時代のライブラリをそのまま使い続け、iOSの新しい機能が実装されていない

[構成]

  • Managers
    • サーバAPIへアクセスする
    • レスポンスをModelへパースする
    • パースしたModelの一部を、インスタンス変数で管理
    • シングルトン
  • ViewController
    • 別ViewControllerに遷移
    • Userのアクションへのリアクション
  • View
    • SnapKitによるレイアウト実装
  • ViewModel
    • Managerにデータを要求、受け取ったデータを管理
    • UIViewControllerにアクセスしてUIの更新等を行う
  • Model
    • データ

[アプリ設計]

[構成]

  • Components
    • Model
    • データ
    • ViewController
    • 別ViewControllerに遷移
    • Userのアクションへのリアクション
    • Delegate / DataSourceの実装
    • Storyboard / Xib
    • レイアウト実装
    • View
    • iPhoneのサイズによるFontサイズの調整など
  • Network
    • APIClient
    • サーバに要求する
    • Responseを生成する
    • Response
    • Codableによって APIをモデル化

比較

Before

After

やったこと

  1. Componentsというディレクトリを作ってその中に各画面毎のfileをいれるようにしました
  • その画面に関係するfileが明確になり、関係ないものは使わないようにチームで心がけるようになりました
  • 各Componentでデザインパターンを変更できるようにしたので、あった設計をできるようにしました
  • 簡単な画面については、コード量が少ない設計に変更
  1. APIClient / JSONデコーダー ともに複数あったので、新しいAPIClientを作成し、Codableを使うように変更 -> 古いものはまとめて削除。Objective-Cの時のような、json["key"] as? Intのような実装をなくしました

  2. ソースコードに対するコメント / BTS なぜその実装になったのか等記載がない -> 現状を知り着手しやすいように、複雑な処理になる部分は、シーケンス図 / コメントで動作を記載

  3. 一行直すだけで、関係ないと思ったところでエラーが起こる -> Component間で扱うデータを減らし、非結合にしてComponent間の影響を減らしました

  4. Manager / Utility クラスは、シングルトンで実装 -> シングルトン実装のクラスを極力減らす。シングルトンにするとpropertyをつけたくなる人がいるので避けます

  5. UIはSnapKitのみで実装 -> Storyboard / Xib で実装、IBInspectable, IBDesignableを使い、GUIで状況を把握しつつ実装  デザイナーが作ってくれているレイアウトに沿った物を作れる ( SnapKitだけの時、cornerRadiusなど漏れが発生していた )

まとめ

よかったこと

  • コードの削除をかなりできた 着手前と今の差分:

    2467 files changed, 142326 insertions(+), 271177 deletions(-)
  • すべての画面に、MVVMをあてようとして無理している部分が多々あり結果、読みにくいコードになっていたのを直せた

  • レビューする際に、コードよりも、Storyboard / Xib で見た方がわかりやすかった Before:

    override func viewDidLoad() {
    super.viewDidLoad()
    .... 40行ほど
    }
    
    After:
    override func viewDidLoad() {
    super.viewDidLoad()
    imageView.image = UIImage(named: ... ) // 1行のみ
    }
    

  • ドキュメント作成した事で効率改善が行えた

    • デザイナーとの画像受け渡しは、Xcodeから直接やってもらえることになった
    • ドキュメント自体もレビューされるので、その際に共有できた
    • レビューする側も、どういう事をしたいロジックなのか理解できた

リファクタリングを行う時、考慮すること

  • チーム全員なので、非エンジニアにも理解してもらわないといけない
  • リファクタしたいところを共有しておく
  • 企画・ディレクター案件がある際、その画面対応の際にまとめて行う
  • ディレクターには、案件を画面毎にまとめるように整理してもらう

e.g. - ホームが重いので改善する作業中に、haptic feedbackの導入や、お気に入りのハートを他の画面と同期するように仕組みを入れました - 検索画面を改修する際に、保存した検索条件に起因する部分もまとめて、書き直し整理を行ったりしました