Experiments Never Fail

Rx で Chain of Responsibility

今さらだけど GoF の Chain of Responsibility パターン。「自分に処理できないタスクは上へ投げる」ってやつ。Reactive な感じでやるとこんな感じかなあと思って書いてみた。

void Main()
{
var document = "有給届";

var kakariCho = CreateManager("係長", document, d => String.Equals(document, "遅刻届"));
var kaCho = CreateManager("課長", document, d => String.Equals(document, "有給届"));
var buCho = CreateManager("部長", document, d => String.Equals(document, "退職届"));

Observable.Concat(new [] {
kakariCho, // 係長
kaCho, // 課長
buCho // 部長
})
.FirstOrDefault() // 最初の1人に承認されたら終了
.Timeout(TimeSpan.FromDays(1)) // 猶予1日
.Subscribe(x => Debug.WriteLine(String.IsNullOrEmpty(x)
? "あなたの届書は却下されました"
: x + "が承認しました"));
}

/// 管理職を作成する(役職名、渡された届書、自分に承認できる届書)
IObservable<string> CreateManager(string managerTitle, string document, Predicate<string> canIAccept)
{
return Observable.Create<string>(o => Task.Run(() =>
{
if (canIAccept(document))
{
o.OnNext(managerTitle); // 承認
}
o.OnCompleted();
}));
}

管理職の人を IObservable に見立てて、自分が処理できるなら OnNext を呼ぶ、処理できないなら OnNext は呼ばずに OnComplete しちゃう。
で、係長・課長・部長の IObservable を Concat で役職の低い順につなげて、 FirstOrDefault() で最初の承認がもらえるまで待つ、みたいな。

係長・課長・部長が誰も承認しなかった時、タイムアウトするまで待ちが発生しちゃうのが難点。→ Take(1) じゃなくて FirstOrDefault すればいいみたい。誰も承認しなかった場合 default(string) つまり空文字が流れてくる。

あと、係長→課長→部長と管理職のハンコリレーが必要な場合に対応できていない、Concat なので係長の結果を課長に引き継いでないから。

んーどうしようか。。。

published at tags: ReactiveExtensions C#