Unity の inspector から interface をアレしたい


## やりたかったこと

a. event をいい感じの observable に変換する handler
b. observer
c. subscribe したときに処理されるなんらか

この b と c わりと一緒のクラスに書いちゃうと思うんですが、分けたい！
b の inspector から c を差し替えられるようにしたい！！

## 例えば

- drag event をいい感じに n 本指ドラッグ、ピンチインアウトの observable にする handler
- observer は2個だけ書けばいい
- ドラッグ、ピンチとでそれぞれ複数の挙動を用意したい
  - 1本指は回転、2本指は移動とか
  - モデルを動かすか、カメラを動かすかとか
- 挙動を足す度に observer 部分を書きたくない！


## uml

![](https://qiita-image-store.s3.amazonaws.com/0/21888/974dce93-9d34-3802-b215-206550429f15.png)

(書き方が正しいかあんまり自信ない)


## 理想

### Observer

```DragObserver.cs
public class DragObserver : MonoBehaviour
{
    [SerializeField] private IDragHandler handler;
    [SerializeField] private int pointerCount;
    
    [Inject] private UIDragHandler dragHandler;
    
    void Start()
    {
        dragHandler.OnDragDeltaAsObservable(pointerCount)
            .Subscribe(handler.OnDrag);
    }
}
```

### interface

```IDragHandler.cs
interface IDragHandler
{
    void OnDrag(Vector2 delta);
}
```

### 実装

```TransformRotator.cs
public class TransformRotator : MonoBehaviour, IDragHandler
{
    public void OnDrag(Vector2 delta)
    {
        Rotate();
    }
}
```

### 現実
![現実](https://qiita-image-store.s3.amazonaws.com/0/21888/e1b343af-795e-b670-f043-87490c15788a.png "現実")

当然 interface は inspector に表示されません。


## どうしても interface を使いたい！案

### `[SerializeField]` を持つ側でよしなにする場合

#### base class

```InterfaceSerializeFieldMonoBehaviour.cs
public class InterfaceSerializeFieldMonoBehaviour<T> : MonoBehaviour
    where T : class
{
    [SerializeField] private MonoBehaviour implementsMonoBehaviour;
    private T _interface;
    protected T Interface
    {
        get
        {
            if (_interface == default(T))
            {
                _interface = implementsMonoBehaviour as T;
            }
            return _interface;
        }
    }

    protected void OnValidate()
    {
        var t = implementsMonoBehaviour as T;
        if (t == default(T))
        {
            implementsMonoBehaviour = null;
        }
    }
}
```

参考: https://forum.unity3d.com/threads/c-interface-wont-show-in-inspector.383886/#post-2498475

#### Observer

```DragObserver.cs
public class DragObserver : InterfaceSerializeFieldMonoBehaviour<IDragHandler>
{
    [SerializeField] private int pointerCount;

    [Inject] private UIDragHandler dragHandler;

    void Start()
    {
        dragHandler.OnDragDeltaAsObservable(pointerCount)
            .Subscribe(Interface.OnDrag);
    }
}
```

### ふむ
![どうしても interface](https://qiita-image-store.s3.amazonaws.com/0/21888/f9f7d9ef-28a6-2f5d-43e3-630a8895a402.png "どうしても interface")

一応 `IDragHandler` を実装していない `MonoBehaviour` は刺せないようにはなっています。

## abstract でお茶を濁す案

### abstract class

```DragObserverBase.cs
public abstract class DragObserverBase : MonoBehaviour
{
    [SerializeField] private int pointerCount;
  
    [Inject] private UIDragHandler dragHandler;

    void Start()
    {
        dragHandler.OnDragDeltaAsObservable(pointerCount)
            .Subscribe(OnDrag);
    }
    
    protected abstract void OnDrag(Vector2 delta);
}
```

### 実装

```TransformRotator.cs
public sealed class TransformRotator : DragObserverBase
{
    protected override void OnDrag(Vector2 delta)
    {
        Rotate(delta);
    }
}
```

### そりゃまあ動く

![abstract](https://qiita-image-store.s3.amazonaws.com/0/21888/682f1c6c-0541-2743-b864-16cbcfa7c478.png "abstract")



## どちらもあまり美しくない

- 今回の場合は abstract でもまあいい気もしてきたけど、 interface で美しく書けないの辛い
- もしかして Unity クソなのでは？
- いやいやお前の頭がクソだよというマサカリ募集中