SOLID原則メモ

オブジェクト指向設計/プログラミングを行う上でよく知られる原則、SOLID原則についてまとめる。

参考
開発者が知っておくべきSOLIDの原則
イラストで理解するSOLID原則

※2022/05/01時点
まとめると書きつつ、今整理したいのはDについてのみなので、他は気が向いたらやる。


SOLID原則とは

オブジェクト指向プログラミングを行う上で、コードの可読性・保守性を高めるための指針として提唱された5つの原則のこと。
もう少し具体的に言うと、カプセル化、拡張可能性、モジュール性などを高めたコードを実装したい。

それぞれ意味は

  • S:Single-responsibility principle、単一責任の原則

  • O:Open/closed principle、開放閉鎖の原則

  • L:Liskov substitution principle、リスコフの置換原則

  • I:Interface segregation principle、インタフェース分離の原則

  • D:Dependency inversion principle、依存性逆転の原則


S:単一責任の原則


O:開放閉鎖の原則

拡張に対してopenである必要があり、かつ修正に対してcloseである必要がある。

参考
オブジェクト指向に限らず守りたい最重要原則 ”open/closed principle - OCP - 開放/閉鎖原則”(SOLID原則より)

開放の原則

拡張に対してopenedであるとはつまり、
機能追加が発生したような場合には既存のコードやクラスには手を加えず新規オブジェクト追加によって対応可能であることが望ましい、ということである。

つまり、新たな機能を追加する際に既存のクラスに手を加えるとなるとテスト実施済のクラスに対して再テストを行わなければならなくなる。新たなバグの可能性も秘めているし、工数的にも有り難くないという感じ。

閉鎖の原則

修正に対してclosedであるとはつまり、
機能修正が発生したような場合にはバグが入ったオブジェクトだけを修正することで対応可能であるべきである、ということである。

コード修正の影響範囲を可能な限り小さくしよう。そのために1つの機能はなるべく1つのクラスに封じ込めるようにしよう、といった感じ。


L:リスコフの置換原則


I:インタフェース分離の原則


D:依存性逆転の原則

a.上位のモジュールは下位のモジュールに依存してはならない。どちらのモジュールも「抽象」に依存すべきである。
b.「抽象」は実装の詳細に依存してはならない。実装の詳細が「抽象」に依存すべきである

参考
依存性の逆転のいちばんわかりやすい説明
【SOLID原則】依存性逆転の原則 - DIP
「依存性逆転の原則」と「依存性の注入」を完全に理解した

ある2つのオブジェクトが依存関係を持つとき、
利用する側のオブジェクトが上位モジュール
利用される側のオブジェクトが下位モジュール
である。

つまり、
あるクラスの機能を使いたければインターフェースを参照しましょうね(&実装を参照しないようにしましょうね)
といったことを言っている。

ぶっちゃけこれだけの話な気がするんだけど「依存性の逆転」という単語を皆さん凄い丁寧に説明しようとしていて、しかもこれがいくら読んでも理解できない


図解

n番煎じだけど図解をしていく。

1.まずは従来の姿。上位モジュールが下位モジュールの実装に依存してしまっている。
例えば上位モジュールがDomainオブジェクトみたいな高安定性を持つべきオブジェクトの管理モジュールである場合、それが別モジュールに依存してしまうことはなるべく避けたい(その分安定性が落ちるので)。

これは分かる。

2.ここから依存性逆転の原則に従ったアーキテクチャ修正に取り掛かる。具体的には上位モジュールがBの実装に依存してしまっている点を、抽象すなわちインターフェースを参照するように変更する。

ただこれだと上位モジュールが下位モジュールを参照してしまっていることには変わりがなく、うっかり誰かがインターフェースではなく実装を上位モジュール側で使ってしまったりすることもまあ考えられるだろう。
それも分かる。

3.ではどうすると良いかという問いに対する一つの解決策として、下位モジュールのインターフェースを上位モジュールで管理するという手法を各記事では取り上げている。

これで上位モジュールが下位モジュールを参照することは無くなり、
上位モジュールも下位モジュールもどちらも抽象に依存するようなアーキテクチャが完成したね。
しかも「上位モジュール→下位モジュール」の依存関係だったものが「上位モジュール←下位モジュール」に逆転し、これがまさに依存性逆転だね。めでたしめでたし。

これが分からない。

いや、というかこれで依存性逆転の原則が達成されるのは分かるけど、下位モジュールのインターフェース「Interface IB」を上位モジュールに配置してしまったことで下位モジュールが上位モジュールを参照する羽目になっているそれは本意なんか???と思ってしまう。

まあそもそも論として依存性を逆転させなければならない理由が分からない。少なくとも依存性逆転の原則のステートメント詳細にそのような事は書いていない気がするのだが。

ステートメントを読む限り上位モジュールと下位モジュールどちらも抽象にのみ依存するような仕掛けを作ればよいのだから、公開インターフェースのモジュールを用意してそれを参照させるようにするとかでも良い。むしろこっちが望ましいのでは?という気がせんでもない。

まあ普通に素人なので分からんのだけど、「逆転」という単語に無理に説得力を持たせようとし過ぎやしていないか?と思う。


おまけ(Wikipediaより)

Wikipediaを読んでみると実際そのように記載されている。

もし低レベルレイヤーがクローズドなコンポーネントであったり、アプリケーションが既存のサービスを再利用する必要がある場合、サービスと抽象レイヤーの間を仲介するAdapterを設けるのが一般的である。
(中略)
抽象コンポーネントをライブラリやパッケージから独立させて置くと、より柔軟性が増す。

https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E9%80%86%E8%BB%A2%E3%81%AE%E5%8E%9F%E5%89%87
従来のレイヤーパターン
依存性逆転パターン

従来のパターンでは上位モジュールが下位モジュールを参照していたところが、上位モジュールと下位モジュール共通用のアダプター的なインターフェースモジュールを参照するような形に変わっている。

やっぱこれでもいいんじゃん。


この記事が気に入ったらサポートをしてみませんか?