【UniRx】MessageBrokerを拡張する

UniRx に付属しているプロセス内の簡易型の広域メッセージ送受信ライブラリのMessageBroker をちょっと拡張してみようと思います。

  • 拡張内容
    • (1) pub/sub は型指定したい
    • (2) 直接 Subscribe したい
    • (3) MessageBroker には触らないで拡張する

確認環境

  • Unity 2021.3.16f1
  • Windows11
  • VisualStudi2022
  • UniRx 7.1.0

実装コード

// IPublisherSubscriber.cs

using System;

namespace UniRx
{
    // 型を指定したpub
    public interface IMessagePublisher<T>
    {
        /// <summary>
        /// Send Message to all receiver.
        /// </summary>
        void Publish(T message);
    }

    // 型を指定したsub
    public interface IMessageReceiver<T>
    {
        /// <summary>
        /// Subscribe typed message.
        /// </summary>
        IObservable<T> Receive();
    }

    // pub/sub共通
    public interface IMessageBroker<T> : IMessagePublisher<T> , IMessageReceiver<T>
    {
        public class DefaultImpl : IMessageBroker<T>
        {
            private readonly IMessageBroker _service;
            public DefaultImpl(IMessageBroker service) => _service = service;
            public void Publish(T message) => _service.Publish(message);
            public IObservable<T> Receive() => _service.Receive<T>();
        }
    }

    // 型の決まったpub/subを取得できるようにメソッドを追加する
    public static class MessageBrokerExtensions
    {
        public static IMessagePublisher<T> GetPublisher<T>(this IMessageBroker self)
        {
            return new IMessageBroker<T>.DefaultImpl(self);
        }
        public static IMessageReceiver<T> GetSubscriber<T>(this IMessageBroker self)
        {
            lreturn new IMessageBroker<T>.DefaultImpl(self);
        }
    }
    
    // 直接Subscribeできるようにメソッドを追加する
    public static class IMessageReceiverExtensions
    {
        public static IDisposable Subscribe<T>(this IMessageReceiver self, Action<T> action)
        {
            return self.Receive<T>().Subscribe(action);
        }
        public static IDisposable Subscribe<T>(this IMessageReceiver<T> self, Action<T> action)
        {
            return self.Receive().Subscribe(action);
        }
    }
}

使い方

フィールドにとるときに型が指定できるようになります。

using UniRx;
using UnityEngine;

// 通知用のクラス
public readonly struct SampleEventArgs
{
    public readonly int Value;
    public SampleEventArgs(int value) => Value = value;
}

// 型を指定して初期化する
IMessagePublisher<SampleEventArgs> _pub = MessageBroker.Default.GetPublisher<SampleEventArgs>();
IMessageReceiver<SampleEventArgs> _sub = MessageBroker.Default.GetSubscriber<SampleEventArgs>();

public void Foo()
{
    // オペレーターを挟む
    _sub.Receive().Take(5).Subscribe(v => Debug.Log(v.Value));

    // 直接購読する
    _sub.Subscribe(v => Debug.Log(v.Value));

    // メッセージを送信する
    _pub.Publish(new SampleEventArgs(10));
}

元々の使い方(参考)

いちおう対比のために元の使い方も記載

using UniRx;
using UnityEngine;

// 型指定しないと若干ガバい
IMessagePublisher _pub0 = MessageBroker.Default;
IMessageReceiver _sub0 = MessageBroker.Default;

public void Bar()
{
    // 使用時に型の指定がないから何でも送信できるので型を間違えたりする可能性あり
    _sub0.Receive<SampleEventArgs>().Subscribe(v => Debug.Log(v.Value));
    _pub0.Publish(new SampleEventArgs(10));

    // 結局こうしたくなる → staticの直接参照はあんまりよくない
    MessageBroker.Default.Receive<SampleEventArgs>().Subscribe(v => Debug.Log(v.Value));
    MessageBroker.Default.Publish(new SampleEventArgs(10));
}
}