Service Locator、Lazy、Activator の調査

C# のコードリーディングをしているときに、Service Locator が出てきて、「どんなパターンだっけ？」と思ったので、しっかり理解するために、サンプルを書いてみた。また、そこに出てきた `Lazy`, `Activator` のテクニックに関しても調べてみた。

# オリジナルのパターン

確かオリジナルは、J2EE patterns じゃないかと思う。このパターンは、サービスをファクトリするのだけど、`ServiceLocator` が直接Serviceの実装クラスをファクトリするのではなく、`InitialContext` が生成や、リモートコールへのリファレンスを返すようにするのがツボになっている。また、サービスの参照に関しては、キャッシュにより保持されるので、二回目からの呼び出しからはキャッシュが使われる。特に実装クラスが、リモートコールの場合はパフォーマンスも改善されるという具合だ。

![クラス図0.jpg](https://qiita-image-store.s3.amazonaws.com/0/3470/9dffeb0f-d4bd-2cce-a5d2-9acc57af877a.jpeg)

# 現在の実装

ただ、J2EE Patterns は随分古いパターンで、当時は EJB という仕組みがあった。その名残を引き継いでいる。しかし、言語も進化しているので、現在ではもう少しクールに実装できる。InitialContext とか、キャッシュが消えている。しかし、パターンの本質は変わらないようになっている。次からコードを見ていこう。


![クラス図0.jpg](https://qiita-image-store.s3.amazonaws.com/0/3470/e0705627-3d5c-d9eb-09a6-78bfd41a0f60.jpeg)


# コードの解説

## Lazy 句

Lazy 句を使うと、Lazy Initialization が可能になる。どういうことかとういと、例えばリモートコールをしてデータを取得するメソッドがあるとして、そのメソッドを毎回呼んでいたらコストがかかる。もしかすると、使われないかもしれない。そういう時に、メソッドが本当に呼ばれたときだけ、その中身を実行するというイディオムだ。

Lazy 句を使うとそれが実装できる。ここでいうと、Singleton のインスタンスを初期化するときに、Lazy 句を使っているが、`() => new ServiceLocator();` が実際に実行されるのは、`instance.Value` が呼ばれたタイミングになる。つまり、`*.Value` が呼ばれない限りは、Lazy の引数の中の関数は実行されない。


```csharp
public class ServiceLocator
{
    static readonly Lazy<ServiceLocator> instance = new Lazy<ServiceLocator>(() => new ServiceLocator());
       :
     public static ServiceLocator Instance => instance.Value;

```

　Lazy 句のもう一つ良いところは、スレッドセーフであるということだ。例えば、このような仕組みを実装する場合、instance が null かチェックするメソッドを書いて、nullならインスタンス生成とかもできるが、そうすると、マルチスレッドの環境では、タイミングが悪ければ何回もインスタンス化が走ることになる。
　Lazy の実装では、しっかりロックをかけてくれるので自分で排他制御を実装する必要がないところがイケている。

## Activator 句

もう一つの自分にとっての新しい構文は、`Activator` 句だ。これはいわゆるリフレクションで、引数の型のインスタンスを初期化するようになっている。ここでは、ジェネリクスで、サービスのインターフェイスと、実装クラスを指定すると、ディクショナリに、登録してくれるようになっている。

```csharp
        public void Register<ServiceInterface, ServiceImplementation>() where ServiceImplementation : new ()
        {
            registeredServices[typeof(ServiceInterface)] =
                new Lazy<object>(() => Activator.CreateInstance(typeof(ServiceImplementation)));
        }
```

実際使うときは `GetService<T>` を使う。ここで、私は、`IService` じゃないんだー。と思ったけど、ジェネリクスで書いているので、この ServiceLocator は、`IService` にすら依存していない。だからクラス図も依存しないように書いている。

```csharp
        public T GetService<T>() where T : class
        {
            Lazy<object> service;
            if (registeredServices.TryGetValue(typeof(T), out service))
            {
                return (T)service.Value;
            }
            throw new ArgumentException("Specified Service not found");
        }
```


使う側はこんなコードになる。DIしているのに近いイメージ。

```csharp
            ServiceLocator.Instance.Register<IService, OsakaService>();
            var service = ServiceLocator.Instance.GetService<IService>();
            Console.WriteLine(service.Greeting());
            Console.ReadLine();
```

結果は

```
もうかりまっか
```

になる。コードの全体像は次のとおり。

* [ServiceLocator Sample](https://github.com/TsuyoshiUshio/ServiceLocatorSample)

よし、しっかり理解できたかな。

# Resources

* [ServiceLocator Sample](https://github.com/TsuyoshiUshio/ServiceLocatorSample)
* [Design Pattern - Service Locator Pattern](https://www.tutorialspoint.com/design_pattern/service_locator_pattern.htm)
* [C#でインスタンスをプログラムで作ってみた（Activator.CreateInstance編）](https://water2litter.net/gin/?p=1183)
* [[C#]モデルの遅延初期化の話を書いてみる](https://qiita.com/shinoyu/items/1a3d35d508cf5dfce7d3)