サービスロケーターを作った②

# 始めに
この記事は覚えた内容をアウトプットした記事ですが、日記見たいな気分で書いています。
誤字脱字や覚えた内容が間違っている可能性があるので予めご了承ください。
この記事は下の記事の続きです。

https://qiita.com/nkcGT1Atagawayuuki/items/dc266fdcc3099b5c48b5

# 参考にしたリンク先

https://qiita.com/ozaki_shinya/items/9eb0f827caa6a4108888

上の記事を参考にして作成しています。

# 前回と違う部分
・ロケーターにジェネリック型を持たせるのをやめた
・番号管理をやめた

# 前回から得た知識

サービスロケータはどのタイミングで利用するのか
・クラス同士の直接参照を避けたい時
・一つしか存在しないクラスを使い回す時

インタフェースを同時に利用すると物凄く便利
・インタフェースに書いたメソッドや数値をロケーターに登録できる
・同時に利用する事で同じ処理インタフェースの処理を使い回せる
・インタフェースの明示実装で他クラスからは値は変更出来ないが利用が出来る

番号管理は凡ミスを起こしやすい
・インスタンスを登録する時や取得する時の引き数に忘れがち
・番号を利用者ごとに決めないとインスタンスを上書きし合う

エラーが分かりにくい
・今までは取得したいコンポーネントがない！のログだけだったのでエラー探しが大変でした

# 実装したコード
沢山コメントを書いたのでコードを見ていただけるとありがたいです。

話が変わりますが、変数やメソッドが時々変な命名になったりするのでいい方法や記事を知っている方、もしよろしければコメントで教えてください！
```C#
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Utility
{
    public static class ServiceLocator
    {
        // 一つしか存在しないインスタンスを登録するディクショナリ
        private static Dictionary<Type, object> singleInstanceDictionary = new Dictionary<Type, object>();
    
        // 利用する度にインスタンスを生成するディクショナリ
        private static Dictionary<Type, Type> generateInstanceDictionary = new Dictionary<Type, Type>();

        /// <summary>
        /// 一つしか存在しないインスタンスをディクショナリに登録する
        /// 同じインスタンスを登録した場合上書きされる
        /// 最初にTransform(1)を登録した後にTransform(2)を登録すると上書きされ、
        /// GetInstanceでTransfromを取得すると(1)ではなく上書きされた(2)を取得する事になる。
        /// </summary>
        public static void Register<T>(T instance) where T : class
        {            
            // Tをディクショナリに登録する
            singleInstanceDictionary[typeof(T)] = instance;
        }

        /// <summary>
        /// 型をディクショナリに登録する
        /// このメソッドで登録したインスタンスをGetInstanceで取得する度に、新しくインスタンスが生成される。
        /// ここで登録したインスタンスをGetInstanceされる度に新しく生成するので、連続使用はおすすめしない。
        /// ここで登録したインスタンスが上のメソッドで既に登録されている場合新しく作成する事は出来ない。
        /// </summary>
        /// <typeparam name="TRegisterClass">登録したいクラス</typeparam>
        /// <typeparam name="TGetRegisterClass">登録したいクラスを取得するクラス</typeparam>
        public static void Register<TRegisterClass, TGetRegisterClass>()
        {
            // ディクショナリに登録した場所にTを渡す
            generateInstanceDictionary[typeof(TRegisterClass)] = typeof(TGetRegisterClass);
        }

        /// <summary>
        /// サービスロケーターに登録したインスタンスを取得する。
        /// もし登録していなかった場合ログを出し、インスタンスにはdefaltを返す。
        /// </summary>
        public static T GetInstance<T>() where T : class
        {
            T instance = default;
            Type type = typeof(T);

            // 一つしか存在しないインスタンスなら中の処理を行う
            if(singleInstanceDictionary.ContainsKey(type))
            {
                instance = singleInstanceDictionary[type] as T;
                return instance;
            }

            // 生成するインスタンスなら中の処理を行う
            if(generateInstanceDictionary.ContainsKey(type))
            {
                // インスタンスに新しく生成したTを入れる
                instance = Activator.CreateInstance(type) as T;
                return instance;
            }

            
            // 登録されていないインスタンスの場合ログを出す
            if(instance == null)
            {
                Debug.LogWarning($"ServiceLocatorには{typeof(T).Name}が存在しません");
            }

            return instance;
        }

        /// <summary>
        /// ディクショナリに登録したインスタンスを全て破棄する
        /// </summary>
        public static void AllClear()
        {
            singleInstanceDictionary.Clear();
            generateInstanceDictionary.Clear();
        }
    }
}
```
# インスタンスの番号管理について
上の方でデメリットしか書いていないですが、正直便利です。（使いにくいですが）
デメリットは上に書いてある通りですがもちろんメリットもあります。

# 番号管理のメリット
・保存先を分ける事で同じインスタンスを登録する事が出来る
・別々に分けているので特定のインスタンスを取得する事が可能
プレイヤーのTransform欲しいとかエネミーのTransfrom欲しいなど登録先を分けていたのでこういう事も出来ました。

# まとめ
・クラスの直接参照を避けたい時にロケーターを利用すると便利
・インタフェースを同時に利用する事で値は変更出来ないが利用は出来るを実現できる
・インスタンスの番号管理は使いにくいが便利ではある