コンポーネントについて少し考える


コンポーネントの原則

SOLID…レンガを組み合わせて壁や部屋を作る方法
コンポーネントの原則…部屋を組み合わせて建物を作る方法

コンポーネント

  • デプロイの単位
  • 単体でもデプロイでき、システムの一部でもデプロイでき、複数のコンポーネントをリンクして実行可能ファイルを作れたり、.exe のように動的に読み込むプラグインとしてもデプロイできる。
  • 優秀なコンポーネントは常に個別にデプロイできる
  • ディスクの高速化や安価に手に入るようになったり、小型化されることでデータの I/O が爆速で向上したおかげで、プログラム量が増えることを気にしたり、コンパイルに数時間・数日かかることはなくなった

コンポーネントの凝集性

再利用・リリース等価の原則(REP)

リリースには適切な通知とドキュメントが必要

また、tag とかリリース番号とかで追跡できることも重要

コンポーネント

  • クラスやモジュールを適当に寄せ集めたものではない
  • 一貫するテーマや目的がありそれを共有するためのモジュールを集める必要がある

一つのコンポーネントを形成するクラスやモジュールはまとめてリリース可能であること

閉鎖性共通の原則(CCP)

SRP のコンポーネント版

SRP(復習)

  • クラスを変更する理由が複数あるべきではない

例えば本を管理するアクターとして、司書、利用者が考えられるが、一つのクラスを変更したら二つのアクターに影響が出てしまう
以下の例は SRP に違反する

class Book
  def getTitle
    "A Great Book";
  end
end

CCP(閉鎖性共通の原則)

  • コンポーネントを変更する理由が複数あるべきではない
  • 同じタイミングでデプロイされることが多いクラスは一つにまとめてもいい

CCP の「閉鎖性」は OCP の「クローズド」と同じ意味

  • 新規機能や機能拡張をしても既存の実装に影響がないこと(OCP)
  • 変更の種類が似ているクラスを一つのコンポーネントにまとめる(CCP)

何か良い例がないものか…

SRP との関係

SRP…複数アクターに影響が出るのであれば別のクラスにメソッドを分ける
CCP…変更の理由が異なるクラスは別のコンポーネントに分ける

要は同じタイミングで変更したり、同じ理由で変更するものについてはひとまとめにしてしまうということ

全再利用の原則(CRP)

一緒に用いられることが多いクラスやモジュールは同じコンポーネントにまとめよ

A というコンポーネントが B というコンポーネントを用いるうときに、実際には B というコンポーネントのごく一部の機能を使いたいがために使用するのは避けるべきである

なぜなら、ごく一部の機能を使っているかそこそこ多くの機能を使っているかどうかに関わらず依存は発生し、使われる側(B)のコンポーネントが変更されるたびに使う側(A)のコンポーネントにも変更が必要になるから

Node(npm)や Ruby(gem)を想像すると良さそう

gem のバージョンアップで依存関係が壊れるのを調べやすくした

ISP との関係

ISP…使っていないメソッドを持つクラスに依存しない
CRP…使っていないクラスを持つコンポーネントに依存しない

使っていないクラスを持つコンポーネントに依存しない

→ つまり、使っていない機能を持ったコンポーネント使わないようにしよう

→ ひとまとめにするクラスはどれか?よりも、どのクラスをひとまとめにすべきではないかを伝える原則

まとめ

REP・CCP と CRP は相反する概念である

なぜなら、REP・CCP が出来るだけ一つのコンポーネントにまとめられる場合はまとめてしまおうという思想であることに対して、CRP はひとまとめにすべきでないクラスを伝える原則だからである

そのため、開発時の利便性と再利用性をトレードオフを考慮する必要がある

参考

Clean Architecture 達人に学ぶソフトウェアの構造と設計