web-dev-qa-db-ja.com

多くの依存関係を持つ複雑なオブジェクトをどのように構築すべきですか?

以下のFixAcceptorタイプの構成を検討してください。コードスニペットは、単体テストの一部です。

var logSource = LogSource.OnMessage | LogSource.Event;
var stateStore = new StateStore();

var newOrderSingleMessageValidator = new NewOrderSingleMessageMessageValidator();
var newOrderSingleMessageHandler = new NewOrderSingleMessageHandler(this.logger, newOrderSingleMessageValidator, stateStore);

// More specific message handlers to come ...

var messageHandler = new AcceptorMessageHandler(this.logger, logSource, newOrderSingleMessageHandler);

var messageStoreFactory = new MemoryStoreFactory();
var sessionSettings = new SessionSettings("test_acceptor.cfg");
var logFactory = new FileLogFactory(sessionSettings);

var acceptor = new FixAcceptor(messageHandler, messageStoreFactory, sessionSettings, logFactory);

FixAcceptorの作成feelsは間違っていて混乱しています。特に、将来的にはより具体的なメッセージハンドラーが存在する予定です。

ここで多くの依存関係を新しくしなければならないので、AcceptorMessageHandlerまたはFixAcceptorタイプに適用できる創造的なデザインパターンを探していました。私はビルダーのパターンがマッチする可能性があると思いました。

しかし、チェックリストを読んだ後、ここに提供: ビルダーパターン もうよくわかりません。特に以下の条件を満たさない

  1. 共通の入力と多くの可能な表現が当面の問題であるかどうかを決定します

可能な表現は常に1つだけです。確かに、すべての依存関係を配線して、FixAcceptor型の作成をより簡単にすることができました。しかし、テスト可能性についてはどうでしょうか?

コードについて私が気に入らない他のこと:

  1. 分野横断的な懸念事項/側面を伝えるthis.logger
  2. NewOrderSingleMessageHandlerは、メッセージ検証および処理を担当します。これはSRPの違反ですか?

だから問題は:

ここに私のタイプを構築する最もきれいな方法は何ですか?どのパターンを適用する必要がありますか?

FixAcceptorのコンストラクタ

public class FixAcceptor
{
    private readonly ThreadedSocketAcceptor acceptor;

    // ... 

    public FixAcceptor
    (
        IAcceptorMessageHandler messageHandler, // external type
        IMessageStoreFactory messageStoreFactory, // external type
        SessionSettings sessionSettings, // external type
        ILogFactory logFactory // external type
    )
    {
        this.acceptor = new ThreadedSocketAcceptor
        (
            messageHandler,
            messageStoreFactory,
            sessionSettings,
            logFactory
        );
    }

    // ...
}

NewOrderSingleMessageHandlerのコンストラクタ

// The abstract MessageHandlers implements the validation
public class NewOrderSingleMessageHandler : MessageHandler<NewOrderSingle>
{
    private readonly IStateStore<NewOrderSingle> stateStore;

    public NewOrderSingleMessageHandler
    (
        ILogger logger, 
        IValidator<NewOrderSingle> validator,
        IStateStore<NewOrderSingle> stateStore
    ) : base(logger, validator)
    {
        this.stateStore = stateStore;
    }

    public override void Process(NewOrderSingle message, SessionID sessionId)
    {
        // ...
    }
}

AcceptorMessageHandlerのコンストラクタ

public class AcceptorMessageHandler : MessageCracker, IAcceptorMessageHandler
{
    private readonly ILogger logger;
    private readonly LogSource logSource;
    private readonly IMessageHandler<NewOrderSingle> newOrderSingleMessageHandler;

    public AcceptorMessageHandler
    (
        ILogger logger, 
        LogSource logSource,
        IMessageHandler<NewOrderSingle> newOrderSingleMessageHandler
    )
    {
        this.logger = logger;
        this.logSource = logSource;
        this.newOrderSingleMessageHandler = newOrderSingleMessageHandler;
    }

    // Lots of callbacks following... 
}
1
Matze

あなたはこれをビルダーパターンの主な問題として述べました:

特に以下の条件を満たさない

共通の入力と多くの可能な表現が当面の問題であるかどうかを決定します

これは、この説明が別の問題を解決する「GoFビルダーパターン」を参照しているためです。あなたが探しているのはおそらく Joshua BlochのBuilderパターン で、「テレスコープ型コンストラクタの問題」を解決します。

横断的な関心事/this.loggerの側面を渡します。

代替案は、シングルトンまたは Service Locator のいずれかを使用することです。どちらもアンチパターンと見なされることがよくあります。この特定のケースでは、サービスロケータの使用は問題ないかもしれませんが、その長所と短所を注意深く確認する必要があります。これはトレードオフであり、何が最も効果的かを自分で確認する必要があります。

NewOrderSingleMessageHandlerは、メッセージの検証と処理を担当します。これはSRPの違反ですか?

それは本当に両方に責任がありますか?あなたのコードでは、NewOrderSingleMessageMessageValidatorのインスタンスがNewOrderSingleMessageHandlerに挿入されているようです。そのため、後者は検証タスクを前者に委任しています。これは、SRPの実際の違反のようには見えません。私。

もちろん、あなたのコードが実際に何をしているのかを知らなくても、ここでは推測しかできませんが、これを3つのクラスに分割することは可能かもしれません。

  • NewOrderSingleMessageMessageValidator検証用、

  • NewOrderSingleMessageHandler(未検証のメッセージ処理)、および

  • NewOrderSingleMessageValidatingHandler。以前の各クラスのインスタンスを取得し、それらの検証およびメッセージ処理ジョブを調整します。

それは「本によって」SRPに従います。ただし、注意が必要です。そのようなソリューションを過剰に設計することは容易であり、SRPを徹底的に適用することは、努力するだけの価値がない場合があります。 SRPはそれ自体が目的ではなく、目的を達成するための手段であるため、有用な単体テストを作成するためにその分離が本当に必要かどうかを自分で判断してください。

3
Doc Brown