2022年10月26日水曜日

複数の型のIObservableを効率よく提供するには

仕事でWebSocketクライアントの実装を検討していたときのことです。
WebSocketは文字列とバイナリの2種類のデータを受信できるのですが、これをオブザーバーパターンに適用しようとして困ったことになりました。

public class OldWebSockerClient : IObservable<string>, IObservable<byte[]>
{
private event Action completed = () => { };
private event Action<Exception> error = _ => { };
private event Action<string> nextString = _ => { };
private event Action<byte[]> nextBytes = _ => { };
public IDisposable Subscribe(IObserver<string> observer)
{
completed += observer.OnCompleted;
error += observer.OnError;
nextString += observer.OnNext;
return new Diposable(() =>
{
completed -= observer.OnCompleted;
error -= observer.OnError;
nextString -= observer.OnNext;
});
}
public IDisposable Subscribe(IObserver<byte[]> observer)
{
completed += observer.OnCompleted;
error += observer.OnError;
nextBytes += observer.OnNext;
return new Diposable(() =>
{
completed -= observer.OnCompleted;
error -= observer.OnError;
nextBytes -= observer.OnNext;
});
}
}
public class Diposable : IDisposable
{
private readonly Action _disposeCallback;
public Diposable(Action disposeCallback)
{
_disposeCallback = disposeCallback;
}
public void Dispose()
{
_disposeCallback();
}
}

どちらか片方だけを受信するならこれで良いのですが、両方まとめて受信するクラスを作ると、エラーを完了の通知が2重に通知されて使いにくいものになってしまいます。
しかし文字列とバイナリを同梱するようなクラスを作って通知を1種類にするのも、受信側でわざわざif文を追加させるようなもので非効率ではないでしょうか。
ということでちょっと考えてみました。

public interface IWebSocketListener : IObserver<string>, IObserver<byte[]>
{
new void OnCompleted();
new void OnError(Exception error);
}
public class NewWebSockerClient : IObservable<string>, IObservable<byte[]>
{
// 省略
public IDisposable Subscribe(IWebSocketListener webSocketListener)
{
completed += webSocketListener.OnCompleted;
error += webSocketListener.OnError;
nextString += webSocketListener.OnNext;
nextBytes += webSocketListener.OnNext;
return new Diposable(() =>
{
completed -= webSocketListener.OnCompleted;
error -= webSocketListener.OnError;
nextString -= webSocketListener.OnNext;
nextBytes -= webSocketListener.OnNext;
});
}
}

2種類のIObserverを合体させたインターフェースを用意し、それをオーバーロードしたSubscribeメソッドで受信させるのです。
これなら片方だけ使う場合でも、両方使う場合でも対応させることができます。
両方使う場合のSubscribeメソッドがIObservableに合致したものではありませんが、IWebSocketListenerと対応する感じでインターフェースを作ればそれっぽくなるのではないでしょうか。

…また自分しか得しない記事を書いてしまいました。
次こそは他の方にも役立つ可能性のある技術記事を書きたいものです。

このご時世の体調不良

昨日のことなのですが、妻が急に高熱を出して寝込むという事件が発生しました。
胸の痛みを伴うことから乳腺炎ではないかということで、専門の機関に診てもらうことにしました。
生後4ヶ月の双子を抱えるわが家ではこれだけで大事件ですが、さらなる問題が発生します。
そうです、高熱ということで、新型コロナウイルスに対する何らかの陰性証明が必要となるのです。

電話の結果、抗原検査が陰性であれば診てもらえることになりました。
しかしわが家はPCR検査も抗原検査もまったくしたことがありません。
大慌てで近所の薬局に抗原検査キットがあることを確認し、その日の午後の仕事は休んで買いに走りました。
今は主要な変異株にすべて対応した抗原検査キットが売っていて、割引があったので5回分ほど購入しました。

検査はなんと10分少しで完了、こんなに早いのですね。
結果は陰性で、妻が診察の間は自分が双子の育児を受け持ちました。
赤ちゃんってこういうときに限っていつもより泣き虫になるんですよね。
母親の不調を何かしら感じ取ったのかもしれません。
診察結果も予想通り乳腺炎で、油断はできませんが対策はハッキリしているので何とかなるはずです。

もしもPCR検査が必須と言われていたら、こうスムーズとはいかなかったでしょう。
改めてこのご時世で体調不良になることの恐ろしさを痛感しました。