はじめに
この講座ではプログラミングの設計を勉強する際に避けては通れない「SOLID原則」について学ぶことができます。
SOLID原則は有名ですので名前を知っている人も多いかもしれませんが、全然知らない人でも大丈夫です!
初心者でも分かりやすいようにできるだけ丁寧に解説します。
「Unityの使い方は分かってきたけど、コードが綺麗に書く方法が分からない」
「設計を勉強するといいらしいけど、どうやって勉強したらいいか分からない」
こんな悩みを持っているあなたは、この記事で解決できるかもしれません!
この記事ではUnity公式のサンプルをもとに解説していきます。
公式サンプルのダウンロード方法はこちらの記事を参考にしてください。
SOLID原則とは?
OLID原則とはオブジェクト指向において、拡張性・保守性が高く保つために守る原則のことです。
これは、ソフトウェアエンジニアのRobert C. Martinが提唱した多くの設計原則を5つにまとめたものです。
それぞれの頭文字をとってSOLID原則と呼ばれています。
- S … Single Responsibility Principle: 単一責任の原則
- O … Open-Closed Principle: 開放閉鎖の原則
- L … Liskov Substitution Principle: リスコフの置換原則
- I … Interface Segregation Principle: インターフェイス分離の原則
- D … Dependency Inversion Principle: 依存性逆転の原則
何も考えずにプログラムを書いていると、段々と拡張性・保守性が下がってきます。
- 機能の追加、変更に時間がかかる
- コードに再利用性がない
- 膨大な機能を持ったクラスが出来上がる(こういうクラスを神クラスと言ったりします笑)
- 依存関係が複雑でクラス同士の関係性がハッキリしていない
SOLID原則を学ぶことで、上記のような悩みを解決できる可能性があります。
今回はSOLID原則のD「依存性逆転の原則」を解説していきます。
SOLID原則などの設計に関する学習をしていると「モジュール」という言葉が良く出てきます。
モジュールというと分かりにくいかもですが、C#の場合クラスに置き換えて考えてください。
依存性逆転の原則とは?
これは「重要なクラス(全体に影響するようなクラス)が変更の多いクラス(仕様が変わりわりやす部分など)に直接依存していたら良くないので、抽象に依存することで依存関係を逆転しよう」という原則です。
武器を持つキャラクターを例に考えてみます。
キャラクラスが剣クラスを所持して使っているという場合、キャラクラスは剣クラスに依存していると言えます。
剣クラスを直接所持していた場合、剣クラスに変更があった場合や他の武器に入れえるとなった場合、キャラクラスにまで変更変更が必要になります。
影響範囲の多いキャラクラスに変更を加えるという事は、そこ変更でミスなどがあった場合、影響範囲が広がります。
この原則に従った場合キャラクラスは直接武器クラスを持つのではなく、武器のインターフェイスを持つ形に変えます。
依存関係や抽象などが絡んでくるので、この原則結構ややこしいと思います。
なので、文字での説明はほどほどにして実際にUnityの公式サンプルのコードを見ながら解説していきます。
Unity公式サンプルを見てみよう
Assets/5 DependencyInversion/Scriptsというフォルダの中に4つのスクリプトがあります。
- ISwitchable.cs・・・スイッチを使うオブジェクトに付けるインターフェース
- Door.cs・・・スイッチで開閉するドアクラス
- Trap.cs・・・スイッチで起動するトラップクラス
- Switch.cs・・・スイッチクラス
スイッチを押すと作動するドアとトラップを例にサンプルコードが書かれています。
まずは「依存性逆転の原則」に違反した例から見ていきましょう。
改善前のコード
・ドアクラス
public class Door : MonoBehaviour
{
public void Open()
{
Debug.Log("The door is open.");
}
public void Close()
{
Debug.Log("The door is closed.");
}
}
・スイッチクラス
public class Switch : MonoBehaviour
{
public Door Door;
public bool IsActivated;
public void Activate()
{
if (IsActivated)
{
IsActivated = false;
Door.Close();
}
else
{
IsActivated = true;
Door.Open();
}
}
}
現状の悪い点
現状はSwitchクラスがDoorクラスを所持している(依存している)状態です。
これではスイッチはこのドア専用のクラスになっています。
ほかのスイッチで動作するオブジェクトと連携させるためには、新たにスイッチクラスを作る必要があります。
また現状ではDoorクラスに変更があった際にもSwitchクラスに影響が出てくる可能性があります。
これは「抽象に依存することで依存関係を逆転しよう」というインターフェース逆転の法則反しています。
改善後のコード
・スイッチインターフェース
public interface ISwitchable
{
public bool IsActive { get; }
public void Activate();
public void Deactivate();
}
・ドアクラス
public class Door : MonoBehaviour, ISwitchable
{
private bool isActive;
public bool IsActive => isActive;
public void Activate()
{
isActive = true;
Debug.Log("The door is open.");
}
public void Deactivate()
{
isActive = false;
Debug.Log("The door is closed.");
}
}
・トラップクラス
public class Trap : MonoBehaviour, ISwitchable
{
private bool isActive;
public bool IsActive => isActive;
public void Activate()
{
isActive = true;
Debug.Log("The trap is active.");
}
public void Deactivate()
{
isActive = false;
Debug.Log("The trap is inactive.");
}
}
・スイッチクラス
public class Switch : MonoBehaviour
{
public ISwitchable client;
public void Toggle()
{
if (client.IsActive)
{
client.Deactivate();
}
else
{
client.Activate();
}
}
}
改善後の良い点
改善後はスイッチクラスはISwitchableインターフェースを保持しています。
スイッチと紐図けるオブジェクトはISwitchableインターフェースが付いて入るものであれば自由に変更できます。
またISwitchableインターフェースが変更されない限り、ドアやトラップクラスに変更が入ってもスイッチ側を変更する必要はありません。
この状態は「インターフェース逆転の法則」に則しています。
まとめ
以上が「インターフェース逆転の法則」でした。
最初は分かりにくいかもしれませんが、意識できるようになると依存関係まわりがスッキリします。
徐々に慣れていきましょう!
👇その他の原則を学ぶ
D:依存性逆転の原則←今ここ
👇あなたにお勧めの書籍
デザインパターンが分かりやすくまとめられていて、設計を学ぶ際におすすめです!
コメント