私は、データベースへの接続、データベースからの特定の製品の選択(提供された基準に基づく)、およびこの製品での処理を処理する単純なコンソールアプリケーションを作成しています。このクラスのインスタンスにコマンドライン引数を格納しています。
public class Arguments
{
public string ConnectionString { get; set; }
public int ProductId { get; set; }
public string ProductName { get; set; }
}
ある時点で、データベースから製品をフェッチする必要があります。そのために次のリポジトリを使用しています。
public interface IProductRepository
{
Product GetById(int productId, string connectionString);
Product GetByName(string productName, string connectionString);
}
次に、リポジトリの実装を、それを使用するクラスに注入します。例:
public class ProductProcessor
{
private readonly IProductRepository productRepository;
public ProductProcessor(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
public void Process(Arguments arguments)
{
Product productToProcess;
if (!string.IsNullOrEmpty(arguments.ProductName))
{
productToProcess = productRepository.GetByName(arguments.ProductName, arguments.ConnectionString);
}
else
{
productToProcess = productRepository.GetById(arguments.ProductId, arguments.ConnectionString);
}
// ....
}
}
これは機能していますが、デザインについて私が気に入らないのは、のすべてのメソッドに引数IProductRepository
があることです。connectionString
依存性注入が含まれていなかった場合、おそらく次のように書き直します。
public void Process(Arguments arguments)
{
Product productToProcess;
ProductRepository productRepository = new ProductRepository(arguments.ConnectionString);
if (!string.IsNullOrEmpty(arguments.ProductName))
{
productToProcess = productRepository.GetByName(arguments.ProductName);
}
else
{
productToProcess = productRepository.GetById(arguments.ProductId);
}
// ....
}
これにより、よりシンプルで使いやすいインターフェイスを使用できるようになります。もちろん、現在ProductRepository
はパラメーターなしのコンストラクターがなく、DIコンテナーで使用するのは困難です。理想的には、両方の長所を生かしたいと思います。つまり、ProductRepository
コンストラクターからの接続文字列で初期化し、そのメソッドから接続文字列を削除します。これを達成するための最良のアプローチは何ですか?
私がすでに検討したいくつかのアプローチ:
- 基本的にコンストラクターとして機能するメソッド
Initialize(string connectionString)
をに追加します。IProductRepository
明らかな欠点は、メソッドで何かを行う前に、が呼び出されたかどうかを確認する必要があることですInitialize
。GetById
GetByName
- コンストラクタインジェクションを使用せず、代わりにServiceLocatorパターンを使用してインスタンス化します
ProductRepository
。私はServiceLocatorがあまり好きではありませんが、これはおそらく唯一の可能な解決策です。
より良い代替手段はありますか?
編集:回答から、もう少しコンテキストを投稿する必要があったことがわかります。DIコンテナとしてNinjectを使用しています。Program.csMain
のメソッドで、すべての依存関係をコンテナーに登録し、アプリケーションへのエントリポイントとして機能するクラスをインスタンス化します。
public static void Main(string[] args)
{
StandardKernel kernel = new StandardKernel();
kernel.Bind<IArgumentsParser>().To<IArgumentsParser>();
kernel.Bind<IProductProcessor>().To<ProductProcessor>();
kernel.Bind<IProductRepository>().To<ProductRepository>();
MainClass mainClass = kernel.Get<MainClass>();
mainClass.Start(args);
}
MainClass
外観は次のようになります。
public class MainClass
{
private readonly IArgumentsParser argumentsParser;
private readonly IProductProcessor productProcessor;
public MainClass(IArgumentsParser parser, IProductProcessor processor)
{
argumentsParser = parser;
productProcessor = processor;
}
public void Start(string[] args)
{
Arguments parsedArguments = argumentsParser.Parse(args);
productProcessor.Process(parsedArguments );
}
}
これにより、Ninjectに依存し、グラフ全体を1か所(Main
メソッド)でのみ作成でき、アプリケーションの残りの部分はDIとコンテナーについて何も知りません。
できればそのままにしておきたいです。