話題の記事

Swift版リーダブルコード「関心の分離と単純化のためのSwiftコードの最適化」 #tryswiftconf

2018.03.02

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

おばんです、今朝はWWDC17のジャケットを着てオレンジジュースを飲んで、西海岸を思い出してイキっていた田中です。

このエントリーはtry! Swift Tokyo 2018のセッション 「関心の分離と単純化のためのSwiftコードの最適化」(Javier Soto) のまとめになります。

講演内容

公式サイトより引用。

関心の分離は、コードが再利用されないときには時期尚早な最適化とみなされることがよくありますが、我々がコードが何をしているのかを理解することに対しては大きく影響します。Swiftにおけるこの実例を紹介します。

このセッションの要約

  • コードは書くよりも読む時間の方が長いので、可読性を高める必要がある
  • Swiftはコーディングの原則(Coding Principle)との親和性が高い
  • いくつかの具体例をコードとともに紹介します
    • 紹介する例はTwitterのアプリをイメージするとわかりやすい

講演資料

バックグラウンド

  • 大規模なプロジェクトで働いている
  • コードレビューは大事
    • コードレビューが好きです
    • 同僚のコードを読むと学べる

コーディングの原則を意識する

  • コーディングの原則
    • Simplicity / 単純
    • Conciseness / 簡潔
    • Clarity / 明確
    • これはSwiftのコーディング理念とも通じるものがある
  • 単純化が大事

関心の分離

  • コードは書かれることよりも読まれることの方が多い
    • 自分自身でコードを書くときも、人に聞かれたときに簡潔に答えられるようにすることが大切
  • How から Whatを切り離す
    • その実装が「どう行われているか」でなく「なんなのか」を示す
    • Swiftに適用したときに拡張しやすい作りにする

Example1

  • 文字の数が範囲内かどうかというのを確認するコード

Before

  • sendButton.isEnabled = true となる条件がぱっと見で理解しづらい

After

  • characterCountUsingBackendPolicy という変数に切り出して、 utf16.count に意味をもたせた
  • let characterLimit = 140
  • sendButton.isEnabled = characters <= characterLimit
    • この書き方によって、文字数がリミット内かどうかでsendButtonを押せるかどうかが決まることがわかりやすくなった

Example2

  • リプライ一覧を取得するコード

Before

  • 成功時の結果でブロックしているユーザーのリプライは出さないようにしている
  • ネストも深く、とても複雑で読みにくい

After

  • filteringBlockedContent という変数をprotocol extensionで定義することによって、複雑さをなくせた

Example3

  • NSLayoutAnchorを設定しているコード

Before

  • とても冗長

After

  • 変数に代入するような感覚でanchorを操作できるようにオペレータを実装した
  • より馴染みのある書き方/読み方になった

Example4

  • activeなconstraintに応じてviewのhiddenを管理するコード
  • headerVisible の条件に制約の状態を利用している

Example5

  • セルに代入するテキストを場合分けするコード

Before

  • あらかじめ決められたindexに対して入れる値を場合分けすることは、意味のまとまりがない
  • 冗長である。セルのタイプが増えたら、その分長くなる?

After

  • Rowsをenumで定義して、cellのindexPath.rowに対応させる形でtitleをenumに保持させることでより意図が分割され、明確になった
  • tableView:cellForRowAt 内をスッキリさせられる

Example6

  • UserDefaultsに保持した初回起動フラグによって、処理を分岐させるコード

Before

  • 「チュートリアルを見たことがあるかどうか」という意味だが、UserDefaultsにアクセスしてデータを取って......というのを直接記述しているため可読性が落ちる

After

  • hasSeenTutorialという変数を用意して、「チュートリアルを見たことがあるかどうか」をそのまま表現することができるようになった

Example7

  • iOS 11.0以上の場合、safeAreaLayoutGuideに対して制約を加えるコード

Before

After

  • iOS11とiOS10でsafeareaに制約をつけるかどうかという話
  • safeAreaLayoutGuide

Example8

  • 押しやすい、最低限のサイズのボタンを作っているコード

Before

  • 44 * 44というコードからすぐに意図が伝わる形になっていないので、コードで補足している

After

  • MinimumHitAreaButton という命名でクラス化することでコメントに頼らず意図を伝えられる
  • 「押せる」ということを func hitTest で担保している。(44 * 44以下のサイズのボタンは「押せない」)

Example9

  • ボタンの表示非表示に応じて addSubviewremoveFromSuperview を制御しているコード

Before

  • UI要素ごとにこの設定を行うととても大変

After

  • UIStackViewの、中のUI要素の isHidden がfalseだった場合は非表示にする性質を利用して記述を簡易化している

Example10

  • ProfileViewController
    • user detailsを持つViewControllerではなく
    • どのuserIDのuser detailsを持つかという構成に変更した
  • また、load状態などはenumでState管理することにした

  • これにより、画面の状態管理が読みやすくなった

Summary

  • ローカルスコープの可読性の最適化には価値がある
  • DRY
  • Swift enums are awesome

田中の感想「Swiftらしく読みやすいコードはカッコいい!」

可読性や責務の分離については 『リーダブルコード』 が有名です。簡潔にまとめられた良書として長く、多くの人々に読まれています。

より良い表現の方法は言語によって変わります。Swiftであればenumを活用したり、iOSの領域を含めればUserDefaultsやUIまわりでよく行う実装方法などはいくつかあります。 この発表は明日から使える有用なTipsと、発想を与えてくれました。正直かっこいい。

より良いコードをかくために、手始めに、この発表で紹介された10の方法と考え方を念頭においてコードを改善していこうと思いました。