8

IoCのオブジェクトを設計するための最良の方法に関する提案を探しています

Iocに登録されているDataContextに依存するオブジェクト(Service)があるとします。

ただし、nameプロパティも必要です。次のようにオブジェクトを設計できます。

class Service
{
    public Service(IDataContext dataContext, 
        string name)
    {
        this._dataContext = dataContext;
        this._name = name
    }

    public string Name
    {
        get
        {
            return _name;
        }
    }
}

問題は、名前などの文字列オブジェクトの登録が容易ではなく、Iocコンテナでの使用が複雑になるため、Iocコンテナでの使用が非常に複雑になることです。したがって、解決が混乱します。

var service = Ioc.Resolve<Service>( ?? )

もう1つのアプローチは、次のように設計することです。

class Service
{
   public Service(IDataContext dataContext)
   {
        this._dataContext = dataContext;
   }

    public string Name { get; set; }
} 

解決が簡単になりました。

var service = Ioc.Resolve<Service>();
service.Name = "Some name";

唯一のダウンサイトは、名前を指定する必要がなくなったことです。DIまたはIoCの専門家から、これをどのように設計し、具体的なIocコンテナ技術にかなりとらわれないかを聞きたいと思います。

これをどのように使用するかによって大きく異なりますが、名前が実際にオプションである場合は、オプション2が最適です。ただし、名前が必要な場合は、コードの別のポイントに検証ステップを追加することもできますが、Iocを単純化するための設計を行うこともできます。

考え?

4

5 に答える 5

4

DIコンテナの選択によって、APIの設計が決まることはありません。nameオプションでない場合は、コンストラクターの署名の一部である必要があります(したがって、必須になります)。

次の質問は、大量のオーバーヘッドを発生させずにコンテナを構成する方法になります。その方法はコンテナによって異なります。CastleWindsorで文字列引数の周りに規則を実装する方法は次のとおりです。

public class NameConvention : ISubDependencyResolver
{
    public bool CanResolve(CreationContext context,
        ISubDependencyResolver contextHandlerResolver,
        ComponentModel model, DependencyModel dependency)
    {
        return dependency.TargetType == typeof(string)
            && dependency.DependencyKey == "name";
    }

    public object Resolve(CreationContext context,
        ISubDependencyResolver contextHandlerResolver,
        ComponentModel model, DependencyModel dependency)
    {
        return "foo"; // use whatever value you'd like,
                      // or derive it from the provided models
    }
}

次に、NameConventionを次のようにコンテナに登録します。

container.Kernel.Resolver.AddSubResolver(new NameConvention());

コンテナに適切な拡張ポイントがない場合は、適切な拡張ポイントを選択してください。

于 2011-11-14T17:36:01.297 に答える
3

問題は、名前などの文字列オブジェクトの登録が容易ではなく、Iocコンテナでの使用が複雑になるため、Iocコンテナでの使用が非常に複雑になることです。

最も優れたIoCコンテナーは、構成を行うときにコンストラクター引数を提供する簡単な方法を提供します。

最初の例であるコンストラクターインジェクションは、通常、推奨される方法と見なされます。コンストラクターは、従うべきコントラクトと考えてください。コントラクトが満たされると、有効なオブジェクトがレンダリングされます。

2番目のコードサンプルであるプロパティインジェクションは、通常、コンストラクターインジェクションよりも好ましくないと見なされます。いずれにせよ、IoCコンテナーは通常、構成時にコンストラクターパラメーターまたはプロパティの値を提供する機能を提供します。これらの値は、IoCにそのオブジェクトの作成を要求するたびに提供されます。

使用しようとしているIoCコンテナーはわかりませんが、StructureMapを構成し、さまざまなサービスに文字列値を提供するために使用されるコードのサンプルを次に示します。私があなたの質問を誤解していない限り、これはあなたがやろうとしていることのようです。

ObjectFactory.Initialize(x =>
{
    x.For<ICatalogAdminService>().Use<DLinkCatalogAdminService>()
        .Ctor<string>("catalogConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString)
        .Ctor<string>("contentConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString)
        .Ctor<string>("webCatalogConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_WebConnectionString"].ConnectionString)
        .Ctor<string>("dlinkPromotionAdminConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString);
    x.For<IContentManagementAdminService>().Use<DLinkContentManagementAdminService>()
        .Ctor<string>("contentConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString)
        .Ctor<string>("webCatalogConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_WebConnectionString"].ConnectionString)
        .Ctor<string>("dlinkPromotionConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString);
    x.For<IPromotionAdminService>().Use<DLinkPromotionAdminService>()
        .Ctor<string>("catalogConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString)
        .Ctor<string>("promotionConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString);
    x.For<ISearchService>().Use<Extractor>();
    x.For<IImporter>().Use<Importer>();
    x.For<IOrderAdminService>().Use<DLinkOrderAdminService>()
        .Ctor<string>("contentConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_AdminConnectionString"].ConnectionString)
        .Ctor<string>("orderConnectionString").Is(ConfigurationManager.ConnectionStrings["myCompany_OrdersConnectionString"].ConnectionString);
});

編集

コメントに答えると、実際にコンストラクター引数を手動で指定したい場合は、次のようになります。

ObjectFactory.GetInstance<ICatalogAdminService>(new ExplicitArguments(
    new Dictionary<string, object>() { { "parameter1", "someValue" } }));

明らかに、これは醜い速度になる可能性があるため、これを頻繁に行う場合は、ファクトリ/ヘルパーメソッドをいくつか作成することをお勧めします。

于 2011-11-14T16:27:32.483 に答える
2

このような状況で私が通常行うアプローチは、文字列の代わりに設定オブジェクトを挿入し、コンストラクターでその文字列を表すプロパティを要求することです。または、場合によっては、その文字列プロパティが必要なときはいつでも、その設定から削除して、変更できるようにします(実際にプログラム設定の場合に便利です)。

もう1つのオプションは、バインディングアノテーションのようなものを使用することです。使用している依存性注入フレームワークはわかりませんが、現在使用しているguice(java)フレームワークでどのように実行できるかを次に示します。

于 2011-11-14T16:45:03.983 に答える
1

Castle Windsorを使用している場合は、ここで読むことができる型付きファクトリを使用できます。基本的に、型指定されたファクトリを使用すると、次のようなインターフェイスを作成できます。

public interface IServiceFactory
{
    IService Create(string name);
}

これを挿入し、選択した名前で呼び出すCreate()と、Windsorは構築IServiceされた実装を返します。

于 2011-11-14T16:39:50.077 に答える
1

優れたエンジニアリング手法(SOLIDなど)を念頭に置いて、いつものように設計してください。次に、選択したコンテナが制約を課している場合は、それを適切に使用していないか、間違ったコンテナを使用しています。

Windsorの場合、登録時にコンポーネントにインラインのハードコードされた依存関係を簡単に提供できます。

container.Register(Component.For<Foo>().DependsOn(new{name = "Stefan"});

コンパイル後に変更する必要がある場合は、より動的な依存関係を提供したり、XML構成の値に依存したりすることもできます。

値がオプションの場合は、デフォルト値を指定するコンストラクターを使用するか、コンストラクターのオーバーロードを使用するか、プロパティとして、オプションにします。繰り返しになりますが、現在使用しているコンテナがおそらくより良いコンテナに切り替わらない場合は、優れたコンテナでこれらすべてのケースを処理できます。

于 2011-11-14T21:47:26.277 に答える