Unityで学ぶプログラミングの基本ルール:SOLID原則
はじめに:SOLID原則とは?
ゲームやアプリを作るとき、プログラムがわかりやすくて、直しやすくて、作りやすいととても便利です。そんなときに役立つのが「SOLID原則(ソリッドげんそく)」と呼ばれる5つのルールです。
この資料では、Unityというゲームエンジンを使って、SOLID原則をやさしく学べるように、実際のコード例といっしょに紹介します。
SOLID原則のメリット:
- プログラムが整理されて、あとで直すのが簡単になります。
- 新しい機能を追加するとき、前のコードにあまり触らなくてすみます。
- 同じクラスやスクリプトを、ほかのゲームでも使いやすくなります。
- テスト(うまく動くか確認すること)がしやすくなります。
- チームで作業するとき、役割がはっきりするので、みんなで作りやすくなります。
- 不具合(バグ)が起きたとき、どこを直せばいいか分かりやすくなります。
SOLIDの意味:
- S: 単一責任の原則(Single Responsibility Principle)
- O: 開いていて閉じている原則(Open/Closed Principle)
- L: リスコフの置きかえ原則(Liskov Substitution Principle)
- I: インターフェース分けの原則(Interface Segregation Principle)
- D: 依存の向きを逆にする原則(Dependency Inversion Principle)
原則1:単一責任の原則(SRP)
1つのクラスには、1つの仕事だけをさせよう!
クラスにたくさんの役割を持たせると、あとで変更が大変になります。この原則では、「ひとつのクラス=ひとつの役割」と考えます。
// スコアを管理するクラス
public class ScoreManager : MonoBehaviour
{
private int score = 0;
public void AddScore(int points)
{
score += points;
Debug.Log($"Score: {score}");
}
}
// ゲームの流れを管理するクラス
public class GameManager : MonoBehaviour
{
public ScoreManager scoreManager;
void Update()
{
if (Input.GetKeyDown(KeyCode.S))
scoreManager.AddScore(10);
}
}

スコアの処理はScoreManager、ゲームの流れはGameManagerが担当。
スコアの仕組みを変えたくなっても、ScoreManagerだけ直せばOKです。
原則2:開いていて閉じている原則(OCP)
新しい機能は追加OK。でも、古いコードはなるべく変えない!
この原則では、「拡張はOK、変更はNG」がキーワードです。あとから新しい機能を増やすのに、前のコードを触らずにすむと安心です。
public interface IAttack
{
void Execute();
}
public class MeleeAttack : MonoBehaviour, IAttack
{
public void Execute() => Debug.Log("🔪 ちかづいて攻撃!");
}
public class RangedAttack : MonoBehaviour, IAttack
{
public void Execute() => Debug.Log("🏹 とおくから攻撃!");
}
public class Player : MonoBehaviour
{
public MonoBehaviour attackBehaviour; // MeleeAttack または RangedAttack
private IAttack attack;
void Awake() => attack = attackBehaviour as IAttack;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
attack.Execute();
}
}

新しい攻撃方法(例:魔法攻撃)を追加しても、PlayerクラスはそのままでOK!
原則3:リスコフの置きかえ原則(LSP)
親クラスの代わりに子クラスを使っても、ちゃんと動くようにしよう!
いろんな敵を同じように扱えたら便利ですね。親クラスの「Enemy」と同じルールを守っていれば、どんな子クラスでも使えます。
public abstract class Enemy : MonoBehaviour
{
public abstract void Attack();
}
public class Goblin : Enemy
{
public override void Attack() => Debug.Log("🗡️ ゴブリンの攻撃!");
}
public class Troll : Enemy
{
public override void Attack() => Debug.Log("🔨 トロールの攻撃!");
}
public class EnemyManager : MonoBehaviour
{
public List<Enemy> enemies;
void Start()
{
foreach (var e in enemies)
e.Attack();
}
}

GoblinもTrollも、Enemyとして扱えるから、コードを分けずに一括で使えます。
原則4:インターフェース分けの原則(ISP)
必要な機能だけ、必要な人に!
「全部入り」のインターフェースより、「必要な分だけ」を分けて使った方が使いやすいです。
public interface IMovable
{
void Move(Vector3 direction);
}
public interface IDamageable
{
void TakeDamage(int amount);
}
public class Player : MonoBehaviour, IMovable
{
public float speed = 5f;
public void Move(Vector3 dir) => transform.Translate(dir * speed * Time.deltaTime);
void Update()
{
var dir = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Move(dir);
}
}
public class DestructibleBox : MonoBehaviour, IDamageable
{
public int hp = 3;
public void TakeDamage(int amount)
{
hp -= amount;
if (hp <= 0) Destroy(gameObject);
}
}

プレイヤーは「動く」、箱は「ダメージを受ける」。それぞれ必要なことだけ持っています。
原則5:依存の向きを逆にする原則(DIP)
大事な役割は、細かい仕組みにしばられないように!
細かい実装に直接つながるのではなく、「ルール(インターフェース)」に頼ることで、入れ替えや変更が楽になります。
public interface IAudioService
{
void PlaySound(string clipName);
}
public class AudioService : MonoBehaviour, IAudioService
{
public AudioClip[] clips;
private AudioSource source;
void Awake() => source = gameObject.AddComponent<AudioSource>();
public void PlaySound(string name)
{
var clip = System.Array.Find(clips, c => c.name == name);
if (clip != null) source.PlayOneShot(clip);
}
}
public class SoundManager : MonoBehaviour
{
private IAudioService audioService;
void Awake() => audioService = FindObjectOfType<AudioService>();
public void OnButtonClick() => audioService.PlaySound("Click");
}

SoundManagerは「音を鳴らす」だけを考え、どう鳴らすかはAudioServiceにまかせています。
こうすることで、別の音再生方法にもすぐに差し替えできます。
おわりに:SOLID原則を使って、よいプログラムを作ろう!
この5つの原則は、どれも「読みやすく、直しやすく、安全なコード」を作るための大事な考え方です。
最初はむずかしく感じるかもしれません。でも、少しずつ使ってみることで自然と身につきます。
Unityでのゲーム作りを楽しみながら、SOLID原則も覚えていきましょう!
ディスカッション
コメント一覧
まだ、コメントがありません