凝集度 Cohesion

責任をうまく分担して、自分の責任範囲のことだけやるようにした方がうまくいく、という話。

7種類あって、下に行くほど凝集度が高く、良い状態。

  • 偶発的凝集 Coincidental cohesion …関連性の無い処理をひとつの関数に入れる
  • 論理的凝集 Logical cohesion …論理的に似ている複数の処理をひとつの関数に入れてフラグで処理を分岐する
  • 時間的凝集 Temporal cohesion …特定のタイミング(初期処理とか)にやる、関連性の無い処理をひとつの関数に入れる
  • 手続的凝集 Procedural cohesion …特定のタイミングにやる、実行順序に意味がある複数の処理をひとつの関数に入れる
  • 通信的凝集 Communicational cohesion …同じ引数に順番に適用する複数の処理をひとつの関数に入れる
  • 逐次的凝集 Sequential cohesion …引数を加工して次に渡す複数の処理をひとつの関数に入れる
  • 機能的凝集 Functional cohesion …単一の機能だけをひとつの関数に入れる

時間的凝集から逐次的凝集までの4つは似たような概念なので、実務的には4個をまとめて時間的凝集と考えたほうがすっきりする。

すべての処理が機能的凝集になれば理想的だが、機能的凝集された処理を順番に呼び出すような処理は必ずどっかに現れて、そういう部分は時間的凝集になる。

論理的凝集は極力避けるべし。もし出てきたら、複数の時間的凝集処理に分けるべし。

偶発的凝集はダメ、ぜったい。

結合度 Coupling

グローバル変数をみんなが見るとかやめて、必要なデータだけ引数で渡したほうが、影響が局所化されてうまくいく、という話。

7種類あって、下にいくほど結合度が低く、良い状態。

  • 内容結合 Content coupling …ほかのモジュールの内部状態を直接見ている
  • 共通結合 Common coupling …グルーバル変数を複数の処理が見ている
  • 外部結合 External coupling …プログラム外部のリソースを複数の処理が見ている
  • 制御結合 Control coupling …引数でフラグなどを渡して、ほかの処理の内部分岐を外から制御している
  • スタンプ結合 Stamp coupling …引数で構造体を渡しているが、その中には使われていない要素がある
  • データ結合 Data coupling …処理に使われる引数と返り値だけでデータを受け渡している
  • メッセージ結合 Message coupling …データの受け渡しは無く、ほかの処理を呼び出しているだけ

内容結合、共通結合、外部結合の3つは、グローバル変数的なものをみんなが見てる状態なので、ダメ。

スタンプ結合、データ結合、メッセージ結合の3つは、グローバル変数的なものを無くして引数で受け渡ししてるので、良い。

制御結合はすなわち論理的凝集なので、極力避けるべし。もし出てきたら、複数の時間的凝集処理に分けることで、データ結合やメッセージ結合になる。

DRY (Don’t repeat yourself)

同じ情報を2か所に持つな、同じことを2回やるな、という話。 ただしDRY原則だけで処理を考えると論理的凝集になりやすいので、凝集度も意識しよう。

情報の持ち方をDRYにすると、One Fact in One PlaceとかSSOT (Single Source of Truth)になる。

OAOO (Once and only once)

同じ処理を2回書くな、という話。 ただしOAOO原則だけで処理を考えると論理的凝集になりやすいので、凝集度も意識しよう。

KISS (Keep it simple stupid)

シンプルで単純な設計のほうがうまくいく、という話。

YAGNI (You ain’t gonna need it)

あとで使いそうと思って作ったものは実際にはほとんど使われないので、必要になるまでは追加するな、という話。

SOLID

5つの原則を守って保守しやすい作りにしておこう、という話。

  • 単一責任の原則 Single-responsibility principle …ひとつのクラスが負う責任範囲はひとつにすべき
  • 開放閉鎖の原則 Open/closed principle …あとで機能を追加するとき、既存の実装は触らず、新規実装の追加だけでいけるようにしておくべき
  • リスコフの置換原則 Liskov substitution principle …サブクラスはスーパークラスと置換可能な作りにすべき
  • インターフェース分離の原則 Interface segregation principle …インターフェースは役割ごとに分けて、使わない機能まで実装しなくて済むようにすべき
  • 依存性逆転の原則 Dependency inversion principle …実装を直接使わずにインターフェースを挟むことで、呼び出し先の実装に依存しない作りにすべき

GRASP (General Responsibility Assignment Software Patterns)

9つの原則を守って保守しやすい役割分担にしておこう、という話。

  • 情報エキスパート Information expert …その情報に責任を負っているモジュールに機能を持たせよ
  • 高凝集 High cohesion …凝集度を高めよ
  • 疎結合 Low coupling …結合度を下げよ
  • クリエイター Creator …クラスのファクトリーは、そのクラスを直接使うクラスに持たせよ
  • コントローラ Controller …機能を直接使わずにファサードを挟むことで、依存度を下げよ
  • 間接化 Indirection …機能を直接使わずに仲介者を挟むことで、依存度を下げよ
  • 多態性 Polymorphism …機能の変動する部分はサブクラスに実装し、交換可能にせよ
  • 保護的変容 Protected variations …機能の変動する部分を別クラスに切り出し、凝集度を高めよ
  • 純粋人工物 Pure fabrication …業務ドメインのクラスに持たせにくい機能(エンティティの永続化とか)は、人工的なクラスに切り出すことで凝集度を高めよ

CUPID

次の5つの特徴を持ったソフトウェアは分かりやすくて保守しやすいね、という話。

  • Composable …使いやすい
    • Small surface area …ほどよいサイズのAPI
    • Intention-revealing …意図が分かりやすい
    • Minimal dependencies …ほかへの依存が少ない
  • Unix philosophy …Unixの哲学に従っている
    • A simple, consistent model …ひとつのことだけをうまくやるシンプルな機能の組み合わせ
    • Single purpose vs. single responsibility …責任範囲がひとつというより、目的がひとつ
  • Predictable …予想したとおりに動く
    • Behaves as expected …名前や構造からふるまいが予測できる
    • Deterministic …常に期待通りの動作をする
    • Observable …中で何が起こっているのかが分かる
  • Idiomatic …一般的な習慣に従っている
    • Language idioms …その言語の一般的な習慣に従っている
    • Local idioms …ローカルルールを明示し、一貫して適用している
  • Domain-based …対象の問題領域の習慣に従っている
    • Domain-based language …その問題領域で使われている言葉を使っている
    • Domain-based structure …その問題領域に沿ったディレクトリ構造になっている
    • Domain-based boundaries …問題領域の単位とモジュールの単位が合っている

CQS (Command-Query Separation)

更新処理と照会処理はメソッドを分けよう、という話。データを取得する処理が、副作用としてデータを書き換えたりしてはいけない。

CQRS (Command Query Responsibility Segregation)

更新機能と照会機能はクラスを分けよう、という話。照会機能は、応答性やスケーラビリティを優先して、更新機能とは別のデータソースを見に行ったりする。