iOS Anti Patterns & Refactoring
@kazu0620
自己紹介
https://github.com/kazu0620
坂本 和大( @kazu0620 ) Sansan株式会社 Eight事業部所属
過去に個人で開発したアプリ - 秘密のアルバム(40万DL!) - にゃんこ(15万DL!)
Anti Pattern• 問題に対する、不適切な解決策を分類したもの
• 今日はiOSの開発でよくある10のBad Partsを分類してご紹介します。
Example No.1
UITableViewCellを継承したクラスの初期化処理です。
Exampleは、Objective-Cというイケてる言語で書かれています。
前提
Example No.1
Anti Patternがいくつあるか考えてみましょう。(20秒)
Anti Pattern 1
無意味な変数名の省略
何が問題なのか?
• 一時変数ならまだ良いが、インスタンス変数や寿命が長い変数ならば前後の文脈から意味を推測する必要が出てくる。
• そもそも、省略するメリットは現在の開発環境ではほぼない。
Cocoa向けコーディングライン (by Apple)
• 「使って構わない略語や頭文字」も記述してくれている。
Anti Pattern 2
マジックナンバー
何が問題なのか?
• 実装を読まないと何を意味するのかが理解できない。
• IDEで補完することもできない。
• 誤って別の数値を指定しても気付きづらい
Refactoring
• 定数やEnumを利用して定義しましょう。
• 放置せず見つけたときに変更するべし。
Anti Pattern 3
コードでViewをゴリゴリ書く
何が問題なのか?
• 増え続ける多様なデバイスへの対応へのコスト
• ビルドしないとその座標が意図通りか確認できない
• 今時のAutoLayoutは結構優秀
Refactoring• Storyboadに移行する。慣れてみると
Storyboad+AutoLayoutの方が大体の場合楽でした。
• 経験上、動的に表示を変更するという要件でも、frameをゴリゴリ操作するよりも最小限のConstraintをIBOutletで接続して必要な制約だけ変更する方が楽。
• しかし明らかに割りに合わない時は、仕様上の大きな変更など、replaceできる機を待つのもアリ
Anti Pattern 4
過剰な責務
• 何者にでもなることができるスゴイCell
何が問題なのか?
• DetailCellは、状況に応じて何でも表示している
• どうしてもコードの見通しが悪くなる(永遠と続く
case文やif分岐)
• 新しい振る舞いを追加するとき、変更が必要な箇所が散らばる
• 変更時にデグレが発生するリスクが高くなる。というか実際デグレる。
何が問題なのか?
Refactoring
• 振る舞いごとにClassに切り分けられないか検討する
Anti Pattern 5
責務が限定的でないクラス名
• 抽象的すぎる名前
• 何を責務としているのか見てもわからない
• 本来意図した責務以外の処理を実装されるリスクもある(さっきの過剰な責務の話)
何が問題なのか?
Refactoring
• 責務を限定した名前にリネームする。
• CardDeleteCellなど
• リネームしてビルドを通せば良い
• リスクなく簡単に修正できる
僕がこのコードから見つけた Anti Patternは5つでした!
俺はもっとたくさん見つけたぞ! って方は懇親会で教えてください!
Example No.2
どこがAnti Patternなのか考えてみましょう。(3秒)
Example No.2
どこがAnti Patternなのか考えてみましょう。(3秒)
Anti Pattern 6
Fat View Controller
何が問題なのか?
• 規模が小さいうちは良いが、Fatになるほどにメンテナンスや機能追加の難易度が上がる
• Modelが存在しない(or薄い)ので、状態の管理や監視の実装がどうしても複雑かつ見通しが悪くなってしまう(Model = NSDictionaryなど)
Refactoring
「Objective-C プログラミングの概念」 より
• ModelとViewを仲介する処理
• LifeCycleの管理
以外の処理は別クラスへの切り出しを検討すべき
Refactoring
Modelを切り出す
• NSDictionaryで情報 / 状態を持つコードはメンテナンスがツラい
• Controllerからビジネスロジックを剥がすことが出来る
• 複数のViewControllerで共通のModelを利用できる
Refactoring
Viewを切り出す
• 割とシンプルなViewならStoryBoadでコード無しで作れる
• 必要なUIViewを継承したCustom Viewを作る
• 演出のためのアニメーション等複雑な処理をViewControllerから剥がすことが出来る
Refactoring
その他のリファクタリング
• TableViewやCollectionViewはDataSourceDeleagteを別クラスに切り分けできないか検討する
• チュートリアル / ガイダンスの状態管理などはそれのみを責務としたクラスなどに分けることを検討する
• 複数のModelに対して複雑なデータ操作をしている場合などは、Serviceクラスに切り分けできないか検討する
今回紹介したクラスは、フィード表示という複雑な処理を追加実装しましたが…..
リファクタリングした結果、コード量は逆に500行程度にまで減りました。
その他 よく見るiOSの Anti Pattern のご紹介
Anti Pattern 7Fat App Delegate
• AppDelegateの責務 = アプリ全体のライフサイクルを管理すること
• 本来はそれ以外の処理があるべきではない。が、AppDelegateをGodクラスとして使ってしまう場合がある。
Refactoring
• それぞれの処理を、その責務を持ったクラスに委譲できないか検討する(Logging、APNSなど)
• NSNotificationCenterを利用し、ViewControllerでアプリのライフサイクルイベントを監視する
• 初期起動 -> 画面遷移を行うことを責務としたクラスを作る(Router,
Dispatcherクラス)
Anti Pattern 8Fat Storyboad
何が問題なのか?• コンフリクトする。
Refactoring
• 文脈単位にStoryboadを切り分けることを検討する
• StoryBoard Reference を利用してみる
Anti Pattern 9
不適切なイベント通知
iOSのイベント通知方法
• Delegate
• KVO
• Notification
• Blokcs
Anti Pattern
• とにかく何でもKVO / Notificationで通知する
• 関係が遠いクラスへのイベントをdelegateで通知する
何が問題なのか?• 適切な方法で通知が実装されていないと、どのイベントがどこで発火して、誰が監視しているのかの見通しが悪くなる
• ので、メンテナンスするのが難しいコードになる
Refactoring• 通知元:通知先が1:1の関係の場合はDelegateを利用したほうがわかりやすい(Ex. ViewControllerとViewなど)
• しかし、離れてる場合は、Delegateがクラス間でリレーされてしまい、見通しが悪くなりがち
• 通知元:通知先が1:Manyの関係の場合はNotificationを利用したほうがわかりやすい(Ex. 複数のControllerにModelが状態変化を伝える時など)
Anti Pattern 10
クソコードとか文句言いながら放置する
• 放置すれば状況は必ず悪くなる
• 苦労して悪しきコードを理解したとしても、後任者はまたコードを解読するところから始めることに。
• とはいえ、程度問題。リスクなくリネームだけで解決するなら気づいたときに対応すべき。
何が問題なのか?
The Boy Scout Rule
• ボーイスカウトのルール「来た時よりも美しく
• checkoutしたときより美しくしてpushしよう!
Copyright © Sansan, Inc. All rights reserved.
0
Sansanは一緒に新しい価値を作っていく 仲間をさがしています。
Ruby, Ruby on Rails (Webアプリケーション)
C#,ASP.NET MVC (Webアプリケーション)
iOS / Android アプリ
- 個人向け名刺管理アプリ「Eight」 - 名刺データ化分散処理システム
- 法人向け名刺管理サービス「Sansan」
- 法人向け名刺管理サービス「Sansan」
- 個人向け名刺管理アプリ「Eight」
エンジニア募集中
Sansan 採用 検索
[email protected] まで お気軽にご連絡ください。
興味のある方は