web-dev-qa-db-ja.com

いくつかの異なるコマンドを実行する大きなswitchステートメントを処理する方法は?

サーバー上で常に実行され、サービスバスからメッセージを受信するコンソールアプリケーションをリファクタリングする必要があります。

現時点では、着信メッセージを解析するだけで、プロパティに基づいて、switchステートメントを使用して、さまざまな関数のいずれかを呼び出します(現時点では約70、常に増加します)。問題の1つは、関数が失敗した場合に再試行されないことです。 1つの巨大なswitchステートメントの醜さは言うまでもありません。

これを修正するためにコマンドパターンを使用することに傾いています( https://scottlilly.com/c-design-patterns-the-command-pattern/ )が、pub/subも検討しましたこれらの関数を処理するパターン。

この状況に最適なアーキテクチャパターンは何か知っていますか?

3
Steven

この質問にC#のタグを付けたので、質問に対する言語固有の回答を提供します。 C#とBCLには、コマンドパターンの組み込みバージョンであるデリゲートの辞書が用意されています。持っているのではなく、例えば:

SomeType Foo(MessageType message)
{
    switch (message.Id)
    {
        case "Id1" : return HandleId1();
        case "Id2" : return HandleId2();
        ...
    }
}

あなたはそれを

private readonly Dictionary<string, Func<SomeType>> _handlers =
    new Dictionary<string, Func<SomeType>>
    {
        ["Id1"] = HandleId1,
        ["Id2"] = HandleId2,
        ...
    };

...

SomeType Foo(MessageType message) => _handlers[message.Id]();
4
David Arno

サービスバスを既に使用している場合、アプリを多数のアプリに分割し、メッセージキューをタイプごとのキューに分割します。

これにより、アプリは1種類のメッセージのみを処理し、メッセージを個別にスケーリングして、再デプロイせずに新しいタイプを追加できます。

失敗したメッセージを再試行するためにサービスバスエラーキューを使用できます

1
Ewan

@David Arnoの答えは私がすることですが、1つだけ違いがあります。デリゲートは使用しませんが、コマンドオブジェクトを使用します。したがって、次のようなものになります。

Dictionary<string, ICommand> _handlers = new Dictionary<string, ICommand>();

どこ

public interface ICommand
{
    void Execute();
}

次に、次のようにnullオブジェクトを実装できます。

public NullCommand : ICommand
{
    public void Execute()
    {
    }
}

コードは次のようになります。

ICommand GetCommand(string id)
{
    ICommand retVal = null;
    if (!_handlers.TryGet(id, out retVal))
    {
        retVal = new NullCommand();
    }
    return retVal;
}

これにより、所有権が明確になり、テスト容易性が向上します。

1
Vladimir Stokic