🌱

精読「現場で役立つシステム設計の原則」(1/3)(基本原則編)

2024/12/17に公開


現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法
ドメイン駆動開発をベースとした実践的な設計の原則を学べる良書です。この設計原則が頭に強く刻み込まれるまで繰り返し読み返そうと思います。

小さくまとめてわかりやすくする

プログラムの変更が楽になる書き方

  • わかりやすい名前を使う(省略しない、業務用語と一致させる)
  • 目的ごとに変数を用意する(破壊的代入[1]ではなく説明用の変数の導入[2]
  • メソッドとして独立させる(メソッドの抽出[3]
  • 異なるクラスの重複したコードをなくす
  • 狭い関心事に特化したクラスにする(業務用語と直接対応するドメインオブジェクト[4]を用意する)
  • メソッドは短く、クラスは小さく(メソッドの抽出とクラスの抽出によって、コードを小分け整理するのが、変更を楽で安全にする設計の基本)

小さなクラスでわかりやすく安全に

  • 業務アプリケーションは、データとロジック[5]を最小単位とした組み合わせ
  • 業務アプリケーションをオブジェクト指向で設計する場合には、データの種類ごとに専用の「型」を用意する(=値オブジェクト(Value Object)
  • 値オブジェクトは「不変」にする(完全コンストラクタ[6]で設計する)

複雑さを閉じ込める

  • 配列やコレクションはコードを複雑にするため、データと関連するロジックを1つのクラスに集めて複雑さを閉じ込める(コレクションオブジェクト[7]
  • コレクションオブジェクトは業務の関心事そのもの(売れ筋商品一覧やロイヤルカスタマー一覧など)

場合分けのロジックを整理する

プログラムを複雑にする「場合分け」のコード

  • 区分や種別がコードを複雑にする(顧客区分、製品タイプ等。区分事に異なるロジックを適応するため)
  • 判断や処理のロジックをメソッドに独立させる(else句を使わず、早期リターン[8]にする)
  • 区分ごとのロジックを別クラスに分けて独立性を高めることができる
  • 区分ごとのクラスを同じ「型」として扱う(多態を使うと区分ごとのロジックを整理できるし、if文/switch文を使わずに済む)
  • 列挙型を使った区分オブジェクト[9]が便利
  • 列挙型を使えば、状態の遷移ルールもわかりやすく記述できる

業務ロジックをわかりやすく整理する

データとロジックを別のクラスに分けることがわかりにくさを生む

  • 手続き型の設計であるデータクラス機能クラスに分けては同じ業務ロジックが重複して書かれるため見通しが悪くなる
  • 共通機能ライブラリを用いても、使う側の微妙なニーズの違いなどで使いにくい/メソッドが膨れ上がるため失敗しがち
  • なので、基本方針はデータとロジックを一体にして業務ロジックを整理するのと、三層アーキテクチャの関心事と業務ロジックの分離を徹底する

データとロジックを一体にして業務ロジックを整理する

  • クラス設計で大事なことは、使う側のクラスのコードがシンプルになるように設計すること
  • メソッドをロジックの置き場所にする(ただインスタンス変数を返すだけのgetterメソッドを書かない、必ずメソッドは判断/加工/計算ロジックを実行させる
  • 使う側のクラスに業務ロジックを書き始めたら設計を見直す
  • メソッドを短く書くとロジックの移動がやりやすくなる
  • メソッドは必ずインスタンス変数を使う(そうでないメソッドは、そのクラスのメソッドとしては不適切)
  • クラスが肥大化したら小さく分ける凝集度を高める)
  • クラスの数が増えてきたときの整理の手段がパッケージ

三層の関心事と業務ロジックの分離を徹底する

  • 業務ロジックを小さなオブジェクトに分けて記述する(「注文」は重要な関心事だが大きすぎるため、「商品」「数量」「金額」「納期」「届け先」「請求先」という単位に分けながらドメインオブジェクトを組み立てる)
  • 業務ロジックを俯瞰できるように、ドメインモデル[10]を作る(パッケージ図で表すのも良し)
  • 三層 + ドメインモデルで関心事をわかりやすく分離する(業務ロジックを記述するのはドメインモデルだけ)
脚注
  1. 1つの変数を使いまわして代入を繰り返す ↩︎

  2. 説明用の変数の導入 ↩︎

  3. ロジックとデータをメソッドに独立させる ↩︎

  4. 用語の関心事に対するクラス ↩︎

  5. データを使った演算(判断/加工/計算) ↩︎

  6. オブジェクトの生成時に、オブジェクトの状態を完全に設定してしまうやり方 ↩︎

  7. コレクションを一つだけ持つ専用クラス。要素数のチェックやループ処理はすべてコレクションオブジェクトが実行し、使う側はコレクションオブジェクトのメソッドを呼ぶだけ ↩︎

  8. 例)if(isChild()) return childFee(); ↩︎

  9. 区分定数を単なる定数ではなく、振る舞いを持ったオブジェクトとして表現する ↩︎

  10. 業務で扱うデータと関連する業務ロジックを集めて整理したもの ↩︎

Discussion