はじめに
クラス設計における、コンストラクタの重要性をしっかりと理解すると幸せになります。
DI (Depnecy Injection) などでも重要な要素ではあるので、これを理解していると、安心・安全な設計が可能です。
コンストラクタとは?
コンストラクタとは、クラスがインスタンス化される際に呼び出されるメソッドです。
インスタンスのコンストラクタが正常終了しているということは、そのクラスのインスタンスを安全に利用できるということです。
すなわち、メンバー変数などが安全に利用できるため、呼び出し元が注意さえすれば、そのインスタンスは好きに利用できます。(呼び出し元が注意すればというのは、敢えて、少し含みがある言い回しにしています。)
以下のようなコードを考えてみてください。
class A
{
}
class B
{
public void Func1(A a)
{
Assert(a != null);
...
}
public void Func2(A a)
{
Assert(a != null);
...
}
}
var a = new A();
var b = new B();
b.Func1(a);
b.Func2(a);
上記の例では、B.Func1
や B.Func2
には A
を要求します。
ただ、B
は A
を要求しますが、Func1
や Func2
の呼び出し毎に、新しい A
を必要としません。
A
は不変的なものでよいのです。
この場合に、コンストラクタを利用することが可能です。
class A
{
}
class B
{
readonly A a;
public B(A a)
{
Assert(a != null);
this.a = a;
}
public void Func1()
{
}
public void Func2()
{
}
}
var a = new A();
var b = new B(a);
b.Func1();
b.Func2();
このようにすれば、B
には A
がコンストラクタの呼び出し時点で設定されますので、Func1
や Func2
の呼び出しのたびに A
を指定する必要がなくなります。
また、Func1
や Func2
の呼び出し毎に Assert
チェックも必要なくなっています。
これだけでも、メリットは感じてもらえると思います。
よくあるゲームのプログラムに置き換えれば、
public class GemeManager
{
static GameManager gameManager = new GameManager();
public static GetGamaMnager GetInstance()
=> gameManager;
}
public class GameScene
{
public void Update()
{
if (GameManager.GetInstance() != null) {
return;
}
// GameManager を使用するコード
...
}
}
GameScene gameScene = new GameScene();
gameScene.Update();
上記のコードは、よく見かけるコードですが、これ自体も
public class GameScene
{
GameManager gameManager;
public GameScene(GameManager gameManager)
{
Assert(gameManager != null);
this.gameManager = gameManager;
}
public void Update()
{
// GameManager を使用するコード
...
}
}
GameScene gameScene = new GameScene(GameManager.GetInstance());
上記のようにするだけで、かなり使い勝手が良くなりますし、可読性もグッと上がります。
というか、Update
の呼び出し毎に判定も必要なくなります。
コンストラクタの時点で、そのクラスが必要とするパラメーターを渡すことで、
- 必要とするパラメーターを一覧化することができる
- クラス内のメソッド呼び出し時の安全性が上がる
- 不要なコードを削除できる
などのメリットが、かなりあります。
これらを理解すること、必然的に DI のメリットも理解できます。
余談(上級者向け)
ちなみに冒頭に記載した「呼び出し元が注意すれば」というのは、初期化に関わる部分です。
コンストラクタで指定可能なのは、「インスタンスに対してのみ」であって、「インスタンスの状態」には関与しません。
一例で言えば、「初期化が非同期に行われる」場合には、それを考慮した呼び出し方をしないといけないというものです。
インスタンス化はされているが、初期化が非同期のため、初期化が完了していない可能性もある、ということです。