概要

プログラム設計上の原則である SOLID原則 に関する記事です。個々の原則は何となくわかるものの、それぞれが目指す目的は何か、その手段として何を試みているのか、それによってこの原則ができたのか、といったポイントは聞かれてもサッと出てこなかったりします。それは多分、まだ理解しきれていない部分があるということかなと思います。

今回はより俯瞰的な観点から、それぞれの原則の特徴や立ち位置を分析してみたいと思います。

各技法

Single Responsibility Principle (単一責任の原則)

「これはね、Webも DBも メールも 何でもこなしている便利なサーバーなんだよ。」
「それって下手に触ると全部止まってしまうってことかい?」

SRPはクラスの責務を限定することで、保守の範囲を限定化するための原則です。

SOLID原則 - SRP

Open Closed Principle (開放閉鎖原則)

「このマシンは CPUもメモリもディスクも拡張可能にできているのさ。
 それに変更がケースの内側だけなら、ユーザーや外部デバイスも気づかないだろ?」

OCPは クラスの拡張性を最大化し、デグレードのリスクを最小化するための原則です。

Liskov Substitution Principle (リスコフの置換原則)

「こちらが私の業務を引き継ぐ方です。
 皆さんからの指示は従来どおりで大丈夫です。もちろん同じ結果も保証しますよ。」

LSPは 継承したクラスに対し互換性を保証させるための原則です。

Interface Segregation Principle (インターフェイス分離の原則)

「弁護士の資格と医師の資格、一緒にしたら便利な資格にならないか?」
「おいおい、取得する側の立場にもなってみろよ」

ISPは インターフェイスを適切に分離し実現の負荷を最小化する原則です。

Dependency Injection Principle (依存性逆転の原則)

「明日の会議には翻訳担当と議事担当が必要なんだが、誰に頼めばいい?」
「心配しなくても誰かを着席して待たせておきますから、本業に集中して大丈夫ですよ」

DIPは インターフェイス間の結合を完全に疎にするための原則です。

一覧

実際に一覧にしてみましたが、改めて見るとそれなりに情報量があります。一気に理解しようと思うと厳しいので、徐々に消化してください。

最初に表の項目に関して簡単に説明します。

  • 英名・和名:原則の英名と和名を提示します
  • 原則と実現手段:基本的な原則を太字で記載し、その下に実際にどう実現するかという手法を提示します
  • クラス・継承・I/F:原則を作り込む際に使用する抽象度を(具象クラス・継承関係・I/F実現)の3種に分類して提示します
  • 縦軸の作り込み・横軸の作り込み:原則を作り込む箇所を縦軸(クラスの継承関係)と横軸(クラスの利用関係)で提示します
  • 縦軸の効果・横軸の効果:原則の効果を縦軸(クラスの継承関係)と横軸(クラスの利用関係)で提示します
  • 最終目的:技法は手段ですが その目的は何なのかを改めて振り返ります
# 英名 和名 原則と実現手段 具象 継承 実現(I/F) 縦軸の作り込み 横軸の作り込み 縦軸の効果 横軸の効果 最終目的
1 Single
Responsibility
Principle
単一責任の原則 - “クラス変更する理由は1つ以上存在してはならない"
⇒ 責務に基づいたモジュール単位での処理分割
-
(階層の責務分離)

(境界の責務分離)

(保守スコープ最小化)

(保守スコープ最小化)
保守性の高いロジック分配
2 Open
Closed
Principle
開放閉鎖原則 - “クラスは拡張に対して開いていなければならず 修正に対して閉じていなければならない"
継承による拡張元クラスの変更なき拡張
カプセル化による利用側クラスへの変更なき修正
-
(拡張性を担保)

(カプセル化を担保)

(差分拡張をOpen)
⇒ 安全な拡張

(過剰干渉をClose)
⇒ 安全な修正
保守性の高いクラス構造
- 拡張時
- 修正時
3 Liskov
Substitution
Principle
リスコフの置換原則 - “T型のオブジェクトxに関して真となる属性をq(x)とすると 派生型Sのオブジェクトyについて q(y) が真となる"
⇒ 事前条件を強めない実装 (仕様互換)
⇒ 事後条件を弱めない実装 (結果互換)
- -
(互換性を提供)
- -
(互換性を享受)
継承の互換性保証
- 仕様互換
- 動作互換
4 Interface
Segregation
Principle
インターフェイス
分離の原則
- “クライアントは利用しないメソッドへの依存を強制されない"
⇒ I/F分離により各I/Fの要求メソッドを最小化
- -
(責務を最小化)
-
(実装効率の最適化)
-
(通常のI/Fと同等)
インターフェイス実装時の最適化 (縦方向)
- 実装コストの最適化
5 Dependency
Inversion
Principle
依存性逆転の原則 - “上位レベルのモジュールは下位レベルのモジュールに依存すべきではない。両方とも抽象に依存すべきである"
- “抽象は詳細に依存してはならない。詳細が抽象に依存すべきである"
⇒ 依存方向を整理
⇒ 下位の型を抽象化
⇒ 下位のインスタンスにおける調達・設定プロセスを隠蔽
- - -
(通常のI/Fと同等)

(別途注入の機構を用意)
-
(通常のI/Fと同等)

(注入で具象を隠蔽)
インターフェイス間の疎結合 (横方向)

(○:関連あり, △:関連が薄いが無くはない, - 関連なし)

まとめ

「SOLID原則は設計上の原則」…と単純に片付けるのは簡単ですが、それぞれの原則がどういった構造を用いてどんな価値を提供しているのかを見ると、見えてくるものもあるのかなと思います。特に、クラスレベルでの初期設計に用いる単一責任の原則を除けば、その他はすべて I/F・継承関係による形象化で隠蔽された縦軸の力で、横軸への影響度を限定するものだということが見えてくるのではないでしょうか。

この記事を通じて皆さんの理解が深まれば嬉しいです。何か間違いがあれば指摘して下さい。