ソフトウェア設計

関心の分離(SoC):800行のコンポーネントを解剖してみた感想

800行のビューファイル1つに、すべてが詰め込まれたコードを一度は見たことがあるでしょう。

読了 4 分
関心の分離(SoC):800行のコンポーネントを解剖してみた感想のカバー画像

800行のビューファイル1つに、すべてが詰め込まれたコードを一度は見たことがあるでしょう。

画面を描画するビューコードの中にAPI呼び出しがあり、その横に割引率の計算、下には日付フォーマット関数、途中には分析イベントの送信まであります。何か1つ直すだけでも、800行すべてを読む必要があります。

このようなファイルが破っている原則が、英語でSeparation of Concernsと呼ばれる関心の分離です。開発原則シリーズの最終回は、この原則を扱います。

関心の分離(SoC)とは、プログラムを異なる関心単位に分け、それぞれの部分が1つの関心だけを扱うようにする原則です。

「関心」とは、プログラムが扱う必要のある仕事の種類です。画面の表示方法、データの取得先、ビジネスルールの適用方法は、それぞれ異なる関心です。

この用語を生み出したのはコンピューター科学者のダイクストラ(Dijkstra)で、1974年の論文で次のように表現しました。

一度に1つの側面だけに集中して考えること。それが私の知る唯一の効果的な思考法である。

人間の頭は、一度に複数の関心を扱えません。だからコードも、1つの部分には1つの関心だけを入れるのです。


身近な例:HTML、CSS、JS

Web開発をしたことがあるなら、すでに毎日、関心の分離を使ってきたことになります。

技術 関心
HTML 構造 — 何があるか
CSS 表現 — どう見えるか
JavaScript 動作 — どう反応するか

昔のようにHTMLタグへstyle属性とonclickを大量に書き込むと、ボタンの色を1つ変えるだけでもHTMLを探し回る必要があります。3つのファイルに分ければ、デザイナーはCSSだけ、マークアップ担当者はHTMLだけを見れば済みます。

バックエンドのコントローラー・サービス・リポジトリ構成や、iOSでビュー、ビューモデル、データ層を分けることも、すべて同じ原理です。MVCやレイヤードアーキテクチャは、結局のところ関心の分離を定型化したパターンです。


あの800行ビューを解剖してみる

先ほどの800行ビューを、関心ごとに分けてみましょう。

// Before: すべての関心が1ファイルに
struct ProductPage: View {
    // 関心 1: データ取得(ネットワーク呼び出し、読み込み、エラー処理 150行)
    // 関心 2: ビジネスルール(割引率・在庫計算 200行)
    // 関心 3: 分析イベント(タップ追跡 100行)
    // 関心 4: 画面描画 (body 350行)
}
// After: 関心ごとに居場所を与える
ProductStore(id:)               // データ取得
calcDiscount(product, user)     // ビジネスルール(純粋関数)
Tracker.track("product")        // 分析イベント

struct ProductPage: View {      // 画面描画だけ
    @State private var store: ProductStore

    var body: some View {
        let price = calcDiscount(store.product, user)
        ...
    }
}

分けてみると、メリットは明確です。割引ポリシーが変わったらcalcDiscountだけを見ればよく、この関数は画面と無関係な純粋関数なのでテストも簡単です。API仕様が変わったらProductStoreだけを直せば済みます。800行すべてを読んでいた頃と比べ、変更範囲は10分の1になります。

分けたら変更範囲が10分の1になりました
分けたら変更範囲が10分の1になりました

どこまで分けるべきか?

関心の分離で最も難しいのは、「では、どこに境界を引くのか」です。分けすぎると、数十個のファイルを行き来して読む迷路になります。

私が基準にしているのは、変更の理由です。SOLIDの記事で扱ったSRPと同じ問いです。

この2つのコードは、同じ理由で、同じタイミングに変わるのか?

割引ポリシーと画面レイアウトは異なる理由で変わるため、分離します。一方、ボタンとそのタップハンドラーはほぼ必ず一緒に変わるので、まとめておきます。SwiftUIがビュー単位で構造・スタイル・動作を1ファイルに集めることも、この観点なら矛盾ではありません。技術の種類ではなく、一緒に変わる単位で関心の境界を引き直しているのです。

6つの原則は、結局同じ場所を指していました
6つの原則は、結局同じ場所を指していました

シリーズを終えて

関心の分離で覚えておきたい核心はこれです。

  • コードの1つの部分には、1つの関心だけを扱わせましょう。人間の頭はマルチタスクができません。
  • 実際には、技術の種類ではなく、「一緒に変わる単位」で境界を引くのが現実的です。
  • 分けすぎると迷路になります。分けること自体を目的にしてはいけません。

これで開発原則シリーズは完結です。KISS(シンプルに)、DRY(知識を1か所に)、YAGNI(今必要なものだけ)、SOLID(変更の影響範囲を狭く)、最小驚き(予想どおりに動かす)、そして関心の分離(一度に1つだけ)まで。

振り返ると、6つの原則はすべて同じ場所を指しています。コードはコンピューターではなく人が読むものであり、良いコードとは変更しやすいコードだということです。原則の名前を忘れても、この一文だけは残しておいてください。