概要
グループでSOLID原則とは何かを学んだので、その内容を記載します。
本記事では、SOLID原則の「依存性逆転の原則」について説明します。
SOLID原則とは?
オブジェクト指向で用いられる以下5つの原則のことです。
- Single Responsibility Principle (単一責任の原則)
- Open-Closed Principle (オープン・クローズドの原則)
- Liskov Substitution Principle (リスコフの置換原則)
- Interface Segregation Principle (インターフェイス分離の原則)
- Dependency Inversion Principle (依存関係逆転の原則) ←本記事の内容
変更に強く、理解しやすいソフトウェアを作るための基本原則です。
依存性逆転の原則
実装の詳細を「抽象」に依存させることで、依存関係を逆転させること。
上位モジュールが下位モジュールに依存するのを減らすことを目的としており、
疎結合なコードに繋がります。
疎結合なコードとは?
他のクラスやメソッドとの結びつきが弱い構造になっていること。
疎結合なコードは依存性が少なく、以下のメリットを持ちます。
1.自身の機能を独立して維持できる
2.再利用しやすい
3.テストしやすい
4.変更した際に、他に及ぼす影響が小さい
原則を守らないとどうなるか?
下位モジュールの修正が上位モジュール(依存先)に影響を及ぼす可能性があります。
依存性逆転の原則に則り、下位モジュールが上位モジュールに依存している場合は、下位モジュールのみ修正すれば影響が出ません◎
原則を守った実装方法
1.下位モジュールの抽象化
2.抽象化したものを上位モジュールに移動
3.下位モジュールを上位モジュールが提供する抽象に依存させる
具体的には...
魔法使い(A)が炎攻撃(B)に依存(A→B)
魔法使い(A)は炎攻撃(B)から情報を受け取り、それに基づいて攻撃を行っている状態。
つまり、魔法使い(A)が炎攻撃(B)に依存している状態になります。
でも炎攻撃だけでなく、氷攻撃を使って戦いたい時もある...。
下位モジュールの抽象化
そこで、まずは抽象化を行います。
攻撃(B)のインターフェイスを作成(BI)
炎攻撃の抽象化、つまり攻撃のインターフェイスを作成します。
抽象化したものを上位モジュールに移動
そして、作成したインターフェイスを上位モジュールに移動させます。
魔法使い(A)が攻撃(BI)のインターフェイスを提供(A→BI)
下位モジュールを上位モジュールが提供する抽象に依存させる
最後に、先ほど作成したインターフェイスに下位モジュールが依存するように実装します。
魔法使い(A)が提供する攻撃インターフェイス(BI)に炎攻撃(B)を依存させる((A→BI)←B)
炎攻撃(B)に依存している状態が解消され、氷攻撃も可能に!
実装の詳細(何の魔法を利用して攻撃するか)を「抽象」に依存させることにより「具象」への依存が解決され、疎結合なコードとなっています。
炎攻撃や氷攻撃だけでなくさまざまな種類の魔法に対応することが可能で、これは疎結合なコードのメリットである「再利用しやすい」に当てはまります。
依存の向きに注目すると、(A→B)から(A←B)になっているので、依存性逆転の原則と言います。
抽象クラス、インターフェイスのどちらを用いる?
依存性逆転の原則は理解できたけど、実装時は抽象クラス/インターフェイスどちらを用いて抽象化すべきなの?という声がグループ内であがりました。
私たちのプロジェクトでは、より拡張性が高くなるためインターフェイスを用いる方が良さそうと結論が出ました。
とはいえ、抽象クラスとインターフェイスの元々の役割を考えると、インターフェイスへの依存が適切か疑問が残るため、まだまだ議論の余地がありそうです。