私は、a)パブリックコントラクトのノイズを排除し、b)派生クラスと機能を共有しながら、c)DRYであるコードを記述し、単一責任の原則に留意したい場合に、保護された修飾子を個人的に利用します。典型的なように聞こえますが、例を挙げましょう。
ここに、基本的なクエリハンドラインターフェイスがあります。
public interface IQueryHandler<TCommand, TEntity>
{
IEnumerable<TEntity> Execute(TCommand command);
}
このインターフェイスは、アプリケーションのさまざまなクエリハンドラーによって実装されます。その後、多くの異なるクエリハンドラからのクエリ結果をキャッシュする必要があるとしましょう。ただし、これは実際には単なる実装の詳細です。具体的なクエリハンドラクラスの利用者は、一般的に言って、それについて心配していません。次に、私の解決策は、キャッシングを処理するが、実際のクエリの責任を派生クラスに委ねる実装を作成することです。
public abstract class CachedQueryHandler<TCommand, TEntity>
: IQueryHandler<TCommand, TEntity>
{
public IEnumerable<TEntity> Execute(TCommand command)
{
IEnumerable<TEntity> resultSet = this.CacheManager
.GetCachedResults<TEntity>(command);
if (resultSet != null)
return resultSet;
resultSet = this.ExecuteCore(command);
this.CacheManager.SaveResultSet(command, resultSet);
return resultSet;
}
protected abstract IEnumerable<TEntity> ExecuteCore(TCommand command);
}
CachedQueryHandlerは、他の誰かがExecuteCoreメソッドを直接呼び出すことを意図していません。ただし、クエリがどのように実装されているかも関係ありません。保護された修飾子は、このようなシナリオに最適です。
また、すべてのクエリハンドラーで同じボイラープレートタイプのコードを繰り返したくありません。特に、キャッシュマネージャーのインターフェイスが変更された場合にリファクタリングするのは悪夢であり、これでキャッシュが完全に削除された場合に削除するのは非常に困難です。レベル。
具体的なウィジェットクエリハンドラは次のようになります。
public class DatabaseWidgetQueryHandler : CachedQueryHandler<WidgetCommand, Widget>
{
protected override IEnumerable<Widget> ExecuteCore(WidgetCommand command)
{
return this.GetWidgetsFromDatabase();
}
}
これで、ウィジェットクエリハンドラーのコンシューマーがクエリハンドラーインターフェイスを介してそれを利用する場合、依存性注入を利用すれば確実に強制できますが、CachedQueryProviderクラスに追加されたキャッシュに固有のものは使用されません。その後、必要に応じてキャッシングを追加/削除したり、最小限の労力でキャッシングの実装を完全に変更したりできます。
IQueryHandler<WidgetCommand, Widget> widgetQueryHandler;
var widgets = widgetQueryHandler.Execute(myWidgetCommand);