4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

クラス設計における、コンストラクタの重要性をしっかりと理解すると幸せになります。
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.Func1B.Func2 には A を要求します。
ただ、BA を要求しますが、Func1Func2 の呼び出し毎に、新しい 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 がコンストラクタの呼び出し時点で設定されますので、Func1Func2 の呼び出しのたびに A を指定する必要がなくなります。

また、Func1Func2 の呼び出し毎に 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 のメリットも理解できます。

余談(上級者向け)

ちなみに冒頭に記載した「呼び出し元が注意すれば」というのは、初期化に関わる部分です。
コンストラクタで指定可能なのは、「インスタンスに対してのみ」であって、「インスタンスの状態」には関与しません。
一例で言えば、「初期化が非同期に行われる」場合には、それを考慮した呼び出し方をしないといけないというものです。
インスタンス化はされているが、初期化が非同期のため、初期化が完了していない可能性もある、ということです。

4
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?