LoginSignup
2
4

More than 5 years have passed since last update.

処理の委譲とDIについて

Posted at

処理の委譲とかDIについての記事をいくつか読んだので、具体的な実装を追って理解を深めてみたいと思います。
DIについては軽く触れる程度となります。

複雑なコードができてしまう事例

例として、APIを呼び出してその結果を取得する処理を書いてみましょう。

IEnumerator GetItems()
{
    var url = "https://qiita.com/api/v2/items";
    var www = new WWW(url);
    yield return www;

    var result = Json.Deserialize(www.text);
    Debug.Log(result[0]["title"]);
}

戻り値のJsonをパースして、最初の記事のタイトルを出力する処理ができました。

さて、ここで戻り値の型がMessagePackの場合もあるとします。シンボルを定義して処理の書き分けをしてみましょう。

IEnumerator GetItems()
{
    ...
#if MSGPACK_API
    var result = Msgpack.Deserialize(www.text);
#else
    var result = Json.Deserialize(www.text);
#endif
    Debug.Log(result[0]["title"]);
}

なんと、本番環境では戻り値をgzip圧縮して暗号化することにする、と連絡がありました。対応してみましょう。

IEnumerator GetItems()
{
    ...
#if MSGPACK_API
    var bytes = www.bytes;
#if RELEASE_MODE
    bytes = GZipDecode(Decrypt(bytes));
#endif
    var result = Msgpack.Deserialize(bytes);
#else
    var result = Json.Deserialize(www.text);
#endif
    Debug.Log(result[0]["title"]);
}

……往々にして、このようなコードが出来上がることは良くあります。

処理の委譲

さて、ここで処理の委譲を活用していきましょう。
WWWを引数にresultを取り出す処理が複雑になってしまっていたので、そこをインターフェース化します。

interface IDeserializer
{
    APIResult Deserialize(WWW www);
}

戻り値がJsonの場合、MessagePackの場合、本番環境の場合の3通りのDeserializerを作ってみます。

class JsonDeserializer : IDeserializer
{
    public APIResult Deserialize(WWW www)
    {
        return Json.Deserialize(www.text);
    }
}

class MsgPackDeserializer : IDeserializer
{
    public APIResult Deserialize(WWW www)
    {
        return Msgpack.Deserialize(www.bytes);
    }
}

class APIDeserializer : IDeserializer
{
    public APIResult Deserialize(WWW www)
    {
        var bytes = GZipDecode(Decrypt(www.bytes));
        return Msgpack.Deserialize(www.bytes);
    }
}

IDeserializerを利用して、先ほどの処理を書き直してみましょう。

IDeserializer deserializer;

IEnumerator GetItems()
{
    ...
    var result = deserializer.Deserialize(www.text);
    Debug.Log(result[0]["title"]);
}

#ifによる処理の分岐が消えてスッキリしました!
また、JsonやMessagePack、gzip展開などの処理はIDeserializerが行うことになったため、GetItems()はそれらに依存しなくなりました。

DIについて

先ほどの例ではdeserializerの初期化が省略されていました。これはどこかで定義しておく必要があります。
deserializerを代入する処理まで書いた例を見てみましょう。

class APIClient
{
    public static IDeserializer deserializer;

    public IEnumerator GetItems() { ... }
}

class Program : MonoBehaviour
{
    public void Start()
    {
#if RELEASE_MODE
        APIClient.deserializer = new APIDeserializer();
#elif MSGPACK_API
        APIClient.deserializer = new MsgpackDeserializer();
#else
        APIClient.deserializer = new JsonDeserializer();
#endif

        var client = new APIClient();
        StartCoroutine(client.GetItems())
    }
}

deserializerを外部から代入できるようにして、初期化時に利用するIDeserializerを選択して代入しています。
実はこれはDIパターンとして成立していて、フレームワークなどは利用していませんがDIを実現しています。

このように、場合によって処理が変わる、依存関係をなくして疎結合にするといったことを実現するためにDIが利用されます。
Unityの場合DIフレームワークとしてZenjectというものがあり、単純なDIの他にも便利な機能があるので使い方を覚えてみるのも良いと思います。

2
4
0

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
2
4