オブザーバーパターン

デザインパターンの一つであるオブザーバーパターンについてUnity公式のeBookがでており、気になっていた点を中心に調べてみた。

・公式
https://github.com/Unity-Technologies/game-programming-patterns-demo/
・非公式和訳
https://zenn.dev/twugo/books/21cb3a6515e7b8/viewer/e833a7

※下記サンプルはchatGPTで例示してもらった中でも、分かりやすかったものをピックアップしたもの。
## サンプル
```c#:example.cs
using System;
public class EventExample
{
    public event Action<int> NumberChanged;
    private int _number;
    public int Number
    {
        get => _number;
        set
        {
            if (_number != value)
            {
                _number = value;
                OnNumberChanged();
            }
        }
    }

    private void OnNumberChanged()
    {
        NumberChanged?.Invoke(_number);
    }
}

public class Subscriber1
{
    public void Subscribe(EventExample example)
    {
        example.NumberChanged += OnNumberChanged;
    }

    public void Unsubscribe(EventExample example)
    {
        example.NumberChanged -= OnNumberChanged;
    }

    private void OnNumberChanged(int number)
    {
        Console.WriteLine($"Subscriber1: Number changed to {number}");
    }
}

public class Subscriber2
{
    public void Subscribe(EventExample example)
    {
        example.NumberChanged += OnNumberChanged;
    }

    public void Unsubscribe(EventExample example)
    {
        example.NumberChanged -= OnNumberChanged;
    }

    private void OnNumberChanged(int number)
    {
        Console.WriteLine($"Subscriber2: Number changed to {number}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var example = new EventExample();
        var subscriber1 = new Subscriber1();
        var subscriber2 = new Subscriber2();

        subscriber1.Subscribe(example);
        subscriber2.Subscribe(example);

        example.Number = 42;

        subscriber1.Unsubscribe(example);

        example.Number = 99;
    }
}
```

## アウトプット
```
Subscriber1: Number changed to 42
Subscriber2: Number changed to 42
Subscriber2: Number changed to 99
```

## メリット
##### 1.あるオブジェクトが他のオブジェクトを直接参照することなく、他のオブジェクトに通知できる
EventExampleクラスはSubscriber1クラスを持たないが、EventExampleのNumberプロパティに更新があると、Subscriber1のメソッドが呼ばれるようになっている。
##### 2.「一対多」の関係を簡単に作れて、疎結合を保ちやすい
仮にSubscriber3,Subscriber4と増やしてもEventExampleクラスを編集する必要がない。

## event修飾子について
``` c#
public event System.Action<int> NumberChanged;
```
このeventはprivateやinternalなどと同じアクセス修飾子としての機能を持つみたい。例えば
``` c#:NG
class Program
{
    static void Main(string[] args)
    {
        var example = new EventExample();
        example.NumberChanged();
    }
}
```
とすると、eventによりクラス外から参照できなくなるため、NumberChangedデリゲートを実行できない。もちろん、
``` c#:OK
class Program
{
    static void Main(string[] args)
    {    
        example.NumberChanged += OnNumberChanged;
    }
}
```
は通るので、そこがpublic eventがprivateとで違う点。

## Invoke()
``` c#
NumberChanged?.Invoke(_number);
```
これはInvokeを使っているが、

``` c#
if(NumberChanged != null)
    NumberChanged(_number);
```
と、使わなくても同じ結果になる。nullチェックが簡単にできるのが良さそう。

## delegate
``` c#
public event Action<int> NumberChanged;
```
の部分をdelegateで置き換できる。その場合、

``` c#
public delegate void NumberChangedEventHandler(int number);

public class EventExample
{
    private NumberChangedEventHandler _numberChangedHandlers;

    public event NumberChangedEventHandler NumberChanged
    {
        add => _numberChangedHandlers += value;
        remove => _numberChangedHandlers -= value;
    }

    ...
}

```
と、明示的にデリゲートを宣言する必要がある。
C#言語のバージョンが上がるにつれて、System.ActionやFuncなどのデリゲート型が登場したため、delegateを使用する必要性が少なくなっている。

## EventHandler
System.ActionとEventHandlerは、どちらもイベントハンドラーとして使用されることがあるが、使用する場面や目的が異なる。
例えば、Windows FormsやWPFなどのUIアプリケーションにおいて、Buttonクラスなどのコントロールには、Clickイベントがある。このClickイベントのハンドラとしては、EventHandlerを使用することが一般的。