【学習】複数クラスで止まる理由:依存関係を理解する
はじめに
1つのクラスだけのコードはなんとか写せた。でも Player と Enemy の2つが出てきた瞬間に止まる——これはよくある現象です。
原因は「依存関係」という概念が分かっていないことです。
この記事では、複数のクラスが登場したときに何が起きているのかと、正しい対処を解説します。
複数クラスで止まる場面
例えばこういうコードが出てきます。
class Game
{
void Start()
{
Player player = new Player();
Enemy enemy = new Enemy();
player.Attack(enemy);
}
}このコードを写そうとしたとき、多くの人がここで止まります。
Playerクラスをどこに書けばいい?Enemyクラスはどこに書けばいい?player.Attack(enemy)のenemyはどう渡すの?
依存関係とは何か
「依存関係」という言葉は難しく聞こえますが、意味はシンプルです。
「このクラスがあのクラスを使っている」という関係
Player player = new Player();このコード1行で「Game クラスは Player クラスに依存している」という関係が生まれます。Player クラスが存在しなければ、このコードはエラーになります。
依存関係の図
Game クラス
└── Player クラスを使う(依存)
└── Enemy クラスを使う(依存)
Player クラス
└── Enemy クラスを引数で受け取る(依存)図で見ると分かりますが、Game が中心で、Player と Enemy を使っています。
正しい作成手順
複数クラスが出てきたときの対処は、第4回で学んだ「空クラス戦略」と同じです。
ステップ①:使う側のクラスを先に書く
class Game
{
void Start()
{
Player player = new Player();
Enemy enemy = new Enemy();
player.Attack(enemy);
}
}まずこれを書きます。当然 Player と Enemy がないのでエラーが出ます。
ステップ②:空のクラスを作る
class Player
{
}
class Enemy
{
}これで new Player() と new Enemy() のエラーは消えます。
ステップ③:必要なメソッドを追加する
player.Attack(enemy) のエラーが出ます。Player に Attack メソッドを追加します。
class Player
{
public void Attack(Enemy target)
{
}
}引数に Enemy 型を受け取るようにします。これで player.Attack(enemy) のエラーも消えます。
ステップ④:中身を実装する
エラーが消えたら中身を書きます。
class Player
{
public void Attack(Enemy target)
{
Console.WriteLine("Player attacks Enemy!");
}
}
class Enemy
{
public int hp = 100;
public void TakeDamage(int damage)
{
hp -= damage;
Console.WriteLine($"Enemy HP: {hp}");
}
}複数クラスをどこに書くか
「別のファイルに分けるべき?」と悩む人が多いです。
学習中は同じファイルに書いて構いません。
// Program.cs に全部書いていい(学習中は)
class Game
{
void Start()
{
Player player = new Player();
Enemy enemy = new Enemy();
player.Attack(enemy);
}
}
class Player
{
public void Attack(Enemy target)
{
Console.WriteLine("攻撃!");
}
}
class Enemy
{
public int hp = 100;
}ファイルを分けるのは、コードが大きくなって管理しにくくなってからです。
参照の作り方:3パターン
複数クラスの参照には主に3つのパターンがあります。
| パターン | 書き方 | 使いどころ |
|---|---|---|
| 引数で渡す | player.Attack(enemy) |
一時的に使う場合 |
| フィールドに持つ | this.enemy = enemy |
ずっと参照する場合 |
| 中で生成する | Enemy enemy = new Enemy() |
自分で管理する場合 |
引数で渡すパターン(詳細)
最も理解しやすいパターンです。
class Player
{
public void Attack(Enemy target)
{
// target は引数として受け取った Enemy オブジェクト
Console.WriteLine($"{target} に攻撃!");
}
}呼び出す側:
Enemy enemy = new Enemy();
player.Attack(enemy); // enemy を引数として渡すフィールドに持つパターン(詳細)
継続的に参照する場合はフィールドに持ちます。
class Game
{
Player player; // フィールドとして持つ
Enemy enemy; // フィールドとして持つ
void Start()
{
player = new Player();
enemy = new Enemy();
}
void Update()
{
player.Attack(enemy); // どこからでも参照できる
}
}まとめ
- 複数クラスで止まる原因は「依存関係」が見えていないから
- 使う側のクラスを先に書き、エラーが出たら空クラスを作る
- 学習中は同じファイルに複数クラスを書いて構わない
- 参照のパターンは「引数で渡す」「フィールドに持つ」「中で生成する」の3つ
次の記事
Unity でも全く同じことが起きます。ただし C# と違い、Unity には独自の「置き場所」という概念があります。
→ 第6回:Unityで同じことが起きる理由:置き場所という考え方
シリーズ一覧
| 回 | タイトル |
|---|---|
| ハブ | C#の写経がつらい理由と、正しい学習手順【完全版】 |
| 第1回 | なぜC#の写経はつらいのか:赤い波線の正体 |
| 第2回 | 正しいC#の書き方:namespace→class→methodの順番 |
| 第3回 | プロパティはいつ書くべきか:最後でOKな理由 |
| 第4回 | エラーが出たらどうするか:その場で潰す技術 |
| 第5回(この記事) | 複数クラスで止まる理由:依存関係を理解する |
| 第6回 | Unityで同じことが起きる理由:置き場所という考え方 |
| 第7回 | プログラミングは構造で考える:写経を超えた先にあるもの |