🐙

変化に強いソフトウェア設計のために:SOLID原則

2023/04/30に公開

初めに

先日はこちらの投稿でOOPについて詳細を記述しました。

https://zenn.dev/airiswim/articles/2215300ede2ff6

ここで、オブジェクト指向は保守性と拡張性に強いという話をした。

復習: 保守性、拡張性、再利用性

保守性:

システムやソフトウェアが長期間にわたって運用される場合など、
変更や修正が必要になった際に、それを容易に行えることを指す。
保守性が高いコードは、変更が容易であったり、バグを修正する際に影響範囲が限定されたりするため、開発者やメンテナンス担当者にとって効率的。

拡張性:

システムやソフトウェアが将来的な要件変更や機能追加に対応できることを指す。
拡張性が高いコードは、将来的な変更に対応しやすく、追加開発もしやすいため、
システムの寿命を延ばすことができる。

再利用性:

ある機能や処理を複数の場所で利用することができることを指す。
再利用性が高いコードは、同じ機能を複数の箇所で実装する必要がなく、
開発時間の短縮や保守性の向上に繋がります。

ITの世界は変化が激しいため、しっかりとそこにも長く対応するものを作るためにも、
OOPを理解したコードを書くことが大事です。

これは少々話がずれるが、
"変化が激しい = 社会やビジネスにとって、未来の予測が難しい"
でもあるが、その未来の予測が難しい状況を"VUCA"とも言ったりします。

"VUCA"について
  • Volatility(変動性)
  • Uncertainty(不確実性)
  • Complexity(複雑性)
  • Ambiguity(曖昧性)

これらの頭文字をとった言葉で、ビジネスや組織の環境がますます変化し、
予測がつかない不確実性に満ちた世界を表現している。

技術革新や市場の変化、政治的な不安定要素などがビジネスや組織に大きな影響を与えることがある。
これらの要素は、相互に影響し合い、予測不可能な変化をもたらすため、
組織(人)は常に変化に対応する準備をしなければならない。

したがって、VUCAの時代において、
ビジネスや社会の変化に合わせて柔軟に対応し、新しい技術を積極的に取り入れることが求められる。

変化に強いソフトウェア設計のために
必要になってくる原則の一つ"SOLID原則"について学んでいきましょう。

SOLID原則とは

OOP向けの設計に関して5つの基本原則です。

  • 単一責任の原則 (SRP: single-responsibility principle)
  • 開放閉鎖の原則OCP: open/closed principle)
  • リスコフの置換原則LSP: Liskov substitution principle)
  • インターフェース分離の原則 (ISP: interface segregation principle)
  • 依存性逆転の原則DIP: dependency inversion principle)

単一責任の原則 (SRP: single-responsibility principle)

There should never be more than one reason for a class to change.
変更するための理由が、一つのクラスに対して一つ以上あってはならない.

  • 1つのクラスやモジュールは1つの責務だけを持つべきであるという考え方。

ここでいう責務は、オブジェクトができること (= 機能 )の意味.

  • 1つのクラスやモジュールが担当する責務は明確であり、
    その責務以外のことに関しては関与しないように設計することで、
    コードの可読性、保守性、拡張性を高めることができる。

開放閉鎖の原則(OCP: open/closed principle)

  • ソフトウェアの要素は
    拡張に対しては開かれ、修正に対しては閉じられているべきであるという考え方

=> 新しい機能が必要になった場合には、
既存のコードを修正せずに新しいコードを追加することで実現するべきであり、
既存のコードを修正することで新しい機能を追加することは避けるべきということ。

  • 既存のコードの安定性が保たれ、
    新しい機能を追加する際にも既存のコードに影響を与えることがなくなる。
    新しい機能が追加された場合にも、既存のコードとの整合性を維持することが容易になる

リスコフの置換原則(LSP: Liskov substitution principle)

  • サブタイプは常にそのベースタイプと置換可能であるべきだという考え方。
    簡単に言うと「子クラスは、親クラスの代わりに使えるようにすることが重要」ということ。

=> あるクラスがある場合、そのサブクラスがそのクラスの代わりに使用される場合でも、
プログラム全体の動作が変わらないように設計されるべきであるということ。

あるクラスのメソッドが引数として親クラスのオブジェクトを受け取る場合、
そのメソッドが受け取るオブジェクトが親クラスのオブジェクトであっても、
そのサブクラスのオブジェクトであっても、同じように動作するように設計する必要がある。
(一貫性を保つこと!!)

インターフェース分離の原則(ISP: interface segregation principle)

  • クライアントが必要としないメソッドに依存することを避け、インターフェースを小さく、
    特定の目的に集中させるべきだという考え方。

  • 1つの大きなインターフェースより、複数の小さなインターフェースを使用することが望ましい。
    => クライアントは必要なインターフェースを実装するだけで済み、
    不必要なメソッドに依存することを避けることができる。
    = 新しい機能を追加する場合も、影響を受けるインターフェースが限定されるため、
    変更に強いコードが実現
    できる。

依存性逆転の原則(DIP: dependency inversion principle)

  • プログラムのコンポーネント間の依存関係を管理するための設計原則。

  • 通常、プログラムのコンポーネントは、他のコンポーネントに依存しているが、
    依存関係が不適切に設計されると、コンポーネントの変更が困難になったり、
    予期せぬ影響を与えたりする可能性があります。

  • DIPは、この問題を回避するために、
    高レベルのコンポーネントが低レベルのコンポーネントに直接依存しないようにすることを提唱している。
    代わりに、高レベルのコンポーネントは抽象化されたインターフェースに依存し、
    低レベルのコンポーネントはそのインターフェースを実装することになる。

これにより、コンポーネントの変更が容易になり、
低レベルのコンポーネントを別のものに置き換える場合でも、高レベルのコンポーネントに
影響を与えずに済む。

Discussion