[英]C# - Ninject, IoC and factory pattern
我有一個控制台應用程序,我需要根據用戶的輸入執行某個功能。 如果用戶輸入“功能 1” -> 我執行功能 1,依此類推。
我正在嘗試盡可能干凈和通用地編寫這個項目,並且我想使用IoC和SOLID概念,但我有點卡住了。
到目前為止我所擁有的:
public interface IFeature
{
String execFeature();
}
和
interface IFeatureFactory
{
IFeature createFeature(String input);
}
我的第一個想法是在具體的 Factory 類上設置一個關於用戶輸入的switch case ,並相應地創建具體的Feature ,但我敢打賭有更好的方法來使用IoC 。
我閱讀了 Ninject 工廠擴展,但不明白如何在我的項目中使用它。
使用 IoC/Ninject 進行工廠模式的最佳方法是什么?
如果您的 IFeature 實現沒有其他依賴項,則使用您的方法很好且非常簡單。
例如,假設您有 2 個 IFeature 實現 - SomeFeature 和 OtherFeature ,它們都具有無參數構造函數。
您建議的工廠實現將是這樣的:
public class FeatureFactory: IFeatureFactory
{
IFeature CreateFeature(string input)
{
if(input=="SomeFeature")
{
return new SomeFeature();
}
else
{
return new OtherFeature ();
}
}
}
但是,當您的 IFeature 實現使用這種方法有自己的依賴項時,您就失去了使用 Ninject 和 IoC 的意義。
例如,假設 SomeFeature 看起來像這樣:
public class SomeFeature : IFeature
{
private readonly IDependency1 _dependency1;
private readonly IDependency2 _dependency2;
public SomeFeature (IDependency1 dependency1, IDependency2 dependency2)
{
_dependency1=dependency1;
_dependency2=dependency2;
}
string execFeature()
{
//Some code here...
}
}
和OtherFeature類似...
public class OtherFeature: IFeature
{
private readonly IDependency1 _dependency1;
private readonly IDependency2 _dependency2;
public OtherFeature(IDependency1 dependency1, IDependency2 dependency2)
{
_dependency1=dependency1;
_dependency2=dependency2;
}
string execFeature()
{
//Some code here...
}
}
現在你的工廠會變成這樣:
public class FeatureFactory: IFeatureFactory
{
IFeature CreateFeature(string input)
{
if(input=="SomeFeature")
{
return new SomeFeature(new Dependency1Implementation(), new Dependency2Implementation());
}
else
{
return new OtherFeature(new Dependency1Implementation(), new Dependency2Implementation());
}
}
}
在這里,您可以使用 ninject.extensions.factory 的強大功能,通過使用容器為您解決這些依賴項。(這些依賴項可以有自己的依賴項,並且很快就會變得混亂)。
正如其他人提到的,您可以使用命名綁定來綁定每個 IFeature 實現。
Bind<IFeature>().To<SomeFeature>().Named("SomeFeature");
Bind<IFeature>().To<OtherFeature>().Named("OtherFeature");
當然,您還應該綁定其他依賴項
Bind<IDependency1>().To<Dependency1Implementation>();
Bind<IDependency2>().To<Dependency2Implementation>();
然后使用工廠擴展將 IFeatureFactory 綁定到 Factory。
Bind<IFeatureFactory>().ToFactory();
您需要做的是為 IFeatureFactory 中的每個 IFeature 實現創建工廠方法,並根據名為 binding 的 Feature 將其稱為 Get...。
public interface IFeatureFactory
{
IFeature GetSomeFeature();
IFeature GetOtherFeature();
}
現在 ninject 會為你實現(!)這個類,並且知道為每個方法選擇哪個實現。(不需要服務定位器......)
您可以在客戶端的輸入上使用 switch 語句來選擇要調用的工廠方法,或者您可以將其包裝在某個包含 switch 語句的提供程序類中,在這兩種情況下,您都不必為IFeature 實現自己。
當然,如果需要和其他更復雜的事情,您可以通過工廠方法將參數傳遞給實現構造函數。
我建議您閱讀本文以獲取更多信息。
編輯
我想強調您不必為每個實現編寫工廠方法,您可以對所有實現使用相同的方法(這是可能的,但更復雜)。
為此,您需要創建自定義實例提供程序來檢測要實例化的實現(例如,根據工廠參數),更多信息請參見上面的鏈接和此處。
您可以使用Named Bindings
。 示例代碼:
Bind<IFeature>().To<Feature1>().Named("Feature1");
Bind<IFeature>().To<Feature2>().Named("Feature2");
欲了解更多信息
編輯
如果您不喜歡Service locator pattern
,上述方法不適合您的情況,因為您必須使用IKernel
來解析IFeature
。
IoC 的主要思想之一是您的解決方案的組件之間不應存在依賴關系。 所以這是很好的做法只使用接口和沒有創建關鍵字“新”的類的新實例。 您的問題無法以簡單而優雅的方式解決,因為您只能注入所有功能實現的接口。
所以你有一些特性和它們的實現:
internal interface IFeature
{
}
internal interface IFeature1 : IFeature
{
}
internal interface IFeature2 : IFeature
{
}
還有一個工廠:
internal interface IFeatureFactory
{
IFeature GetInstance(string featureName);
}
internal class FeatureFactory : IFeatureFactory
{
private readonly ITypeFactory<IFeature1> feature1;
private readonly ITypeFactory<IFeature1> feature2;
private readonly Dictionary<string, ITypeFactory<IFeature>> featuresContainer;
public FeatureFactory(ITypeFactory<IFeature1> feature1, ITypeFactory<IFeature1> feature2)
{
this.feature1 = feature1;
this.feature2 = feature2;
featuresContainer = new Dictionary<string, ITypeFactory<IFeature>>
{
{"Feature1", feature1},
{"Feature2", feature1}
};
}
public IFeature GetInstance(string featureName)
{
if (!featuresContainer.ContainsKey(featureName))
throw new Exception(string.Format("Can't create feature {0}", featureName));
return featuresContainer[featureName].Create();
}
}
你可以用這種方式注入所有這些東西:
Bind<IFeatureFactory>().To<FeatureFactory>().InSingletonScope();
Bind<IFeature1>().To<Feature1>();
Bind<IFeature2>().To<Feature2>();
Bind<ITypeFactory<IFeature1>>().ToFactory();
Bind<ITypeFactory<IFeature2>>().ToFactory();
主要思想是您只有一個用於應用程序的特征工廠實例,並且您存儲特征的注入工廠。 因此,當您第一次訪問 IFeatureFactory 時,Ninject 將創建它的單例實例。 但是您的功能實例只會在您調用 GetInstance() 方法時創建。
要使此代碼工作,您應該添加新接口:
public interface ITypeFactory<out T>
{
T Create();
}
並安裝 NuGet 包: https : //www.nuget.org/packages/Ninject.Extensions.Factory/
我必須對 Ninject、IoC 和工廠模式應用以下方法。
步驟 1:添加綁定到 IOC 容器
Bind<IFeature>().To<SomeFeature>().Named(nameof(SomeFeature));
Bind<IFeature>().To<SomeFeature>().Named(nameof(SomeFeature));
第二步:創建一個擴展方法來解決你的依賴
public class Resolver
{
[Inject]
public IKernal kernal { get; set; }
public T Resolve<T>(string type)
{
return kernal.TryGet<T>(type);
}
}
第 3 步創建類的實例
IFeature feature = null;
switch (typeOfFeature)
{
case Feature.SomeFeature:
feature = Resolver.TryResolve<IFeature>(nameof(SomeFeature));
break;
case Feature.OtherFeature:
feature = Resolver.TryResolve<IFeature>(nameof(OtherFeature));
break;
default:
throw new Exception("Type not supported");
}
return feature;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.