以下の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つだけです。確かに、すべての依存関係を配線して、FixAcceptor
型の作成をより簡単にすることができました。しかし、テスト可能性についてはどうでしょうか?
コードについて私が気に入らない他のこと:
this.logger
。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...
}
あなたはこれをビルダーパターンの主な問題として述べました:
特に以下の条件を満たさない
共通の入力と多くの可能な表現が当面の問題であるかどうかを決定します
これは、この説明が別の問題を解決する「GoFビルダーパターン」を参照しているためです。あなたが探しているのはおそらく Joshua BlochのBuilderパターン で、「テレスコープ型コンストラクタの問題」を解決します。
横断的な関心事/this.loggerの側面を渡します。
代替案は、シングルトンまたは Service Locator のいずれかを使用することです。どちらもアンチパターンと見なされることがよくあります。この特定のケースでは、サービスロケータの使用は問題ないかもしれませんが、その長所と短所を注意深く確認する必要があります。これはトレードオフであり、何が最も効果的かを自分で確認する必要があります。
NewOrderSingleMessageHandlerは、メッセージの検証と処理を担当します。これはSRPの違反ですか?
それは本当に両方に責任がありますか?あなたのコードでは、NewOrderSingleMessageMessageValidator
のインスタンスがNewOrderSingleMessageHandler
に挿入されているようです。そのため、後者は検証タスクを前者に委任しています。これは、SRPの実際の違反のようには見えません。私。
もちろん、あなたのコードが実際に何をしているのかを知らなくても、ここでは推測しかできませんが、これを3つのクラスに分割することは可能かもしれません。
NewOrderSingleMessageMessageValidator
検証用、
NewOrderSingleMessageHandler
(未検証のメッセージ処理)、および
NewOrderSingleMessageValidatingHandler
。以前の各クラスのインスタンスを取得し、それらの検証およびメッセージ処理ジョブを調整します。
それは「本によって」SRPに従います。ただし、注意が必要です。そのようなソリューションを過剰に設計することは容易であり、SRPを徹底的に適用することは、努力するだけの価値がない場合があります。 SRPはそれ自体が目的ではなく、目的を達成するための手段であるため、有用な単体テストを作成するためにその分離が本当に必要かどうかを自分で判断してください。