Design patterns 如何使用类似IoC的Ninject实现[GoF]-ish抽象工厂模式 摘要
当设计需要[GoF]所述的“抽象工厂模式”,包括多个产品和多个产品系列时,建立IoC可能会变得有点棘手。特别是当特定的工厂实现需要通过运行时参数进行调度并在一些后续组件之间共享时 考虑到下面的API,我试图设置IoC(本例中为Ninject)以检索通过Design patterns 如何使用类似IoC的Ninject实现[GoF]-ish抽象工厂模式 摘要,design-patterns,ninject,inversion-of-control,named-scope,abstract-factory,Design Patterns,Ninject,Inversion Of Control,Named Scope,Abstract Factory,当设计需要[GoF]所述的“抽象工厂模式”,包括多个产品和多个产品系列时,建立IoC可能会变得有点棘手。特别是当特定的工厂实现需要通过运行时参数进行调度并在一些后续组件之间共享时 考虑到下面的API,我试图设置IoC(本例中为Ninject)以检索通过IConfigurationFactory配置的Configuration对象。该配置存储一个IFactory实例,该实例的实现由ProductFamily类型的运行时参数确定。之后,工厂在配置内部创建的产品类型应始终与请求的ProductFami
IConfigurationFactory
配置的Configuration
对象。该配置存储一个IFactory
实例,该实例的实现由ProductFamily
类型的运行时参数确定。之后,工厂在配置内部创建的产品类型应始终与请求的ProductFamily
相匹配。由组件
类组成的子图按照配置
保存相同的IFactory
public enum ProductFamily { A, B }
public interface IProduct1 { }
public interface IProduct2 { }
public interface IFactory
{
IProduct1 CreateProduct1();
IProduct2 CreateProduct2();
}
public class Configuration
{
public readonly IFactory factory;
public readonly Component component;
public Configuration(IFactory factory, Component component)
{
this.factory = factory;
this.component = component;
}
}
public class Component
{
public IFactory factory;
public Component(IFactory factory) { this.factory = factory; }
}
public interface IConfigurationFactory
{
Configuration CreateConfiguration(ProductFamily family);
}
测验
为了澄清预期的行为,我在vstest中添加了测试代码write。但是正手拍的一些补充,感谢@BatterBackupUnit询问这些细节:
- 工厂只需要
作为一个参数来选择实现,而不需要其他参数ProductFamily
- 每个
及其后续对象(如配置
)共享同一工厂实例组件
ISIAS以下替代方案通过了所有测试,同时保持了相当的通用性。 这些绑定定义了所有配置依赖项。唯一与ninject相关的非绑定代码是IConfigurationFactory,它将必要的配置信息(=>ProductFamily)放在ninject上下文中 您将需要以下nuget包来编译此代码:
- 流畅的断言
- 尼尼特
- Ninject.Extensions.ContextPreservation
- 尼尼特工厂
- Ninject.Extensions.NamedScope
using System.Linq;
using FluentAssertions;
using Ninject;
using Ninject.Activation;
using Ninject.Extensions.Factory;
using Ninject.Extensions.NamedScope;
using Ninject.Modules;
using Ninject.Parameters;
using Ninject.Planning.Targets;
using Ninject.Syntax;
public class Program
{
private static void Main(string[] args)
{
var kernel = new StandardKernel();
kernel.Load<AbstractFactoryModule>();
var configFac = kernel.Get<ConfigurationFactory>();
// create runtime dependent configs
var configA = configFac.CreateConfiguration(ProductFamily.A);
var configB = configFac.CreateConfiguration(ProductFamily.B);
configA.factory.CreateProduct1().Should().BeOfType<Product1A>();
configB.factory.CreateProduct1().Should().BeOfType<Product1B>();
configA.component.factory.Should().Be(configA.factory);
configA.factory.Should().NotBe(configB.factory);
}
}
public enum ProductFamily { A, B }
public interface IProduct1 { }
public interface IFactory
{
IProduct1 CreateProduct1();
}
public class Product1A : IProduct1 { }
public class Product1B : IProduct1 { }
public class Configuration
{
public readonly IFactory factory;
public readonly Component component;
public Configuration(IFactory factory, Component component)
{
this.factory = factory;
this.component = component;
}
}
public class Component
{
public IFactory factory;
public Component(IFactory factory) { this.factory = factory; }
}
public interface IConfigurationFactory
{
Configuration CreateConfiguration(ProductFamily family);
}
public class ConfigurationFactory : IConfigurationFactory
{
private readonly IResolutionRoot resolutionRoot;
public ConfigurationFactory(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public Configuration CreateConfiguration(ProductFamily family)
{
return this.resolutionRoot.Get<Configuration>(new AbstractFactoryConfigurationParameter(family));
}
}
public class AbstractFactoryConfigurationParameter : IParameter
{
private readonly ProductFamily parameterValue;
public AbstractFactoryConfigurationParameter(ProductFamily parameterValue)
{
this.parameterValue = parameterValue;
}
public ProductFamily ProductFamily
{
get { return this.parameterValue; }
}
public string Name
{
get { return this.GetType().Name; }
}
public bool ShouldInherit
{
get { return true; }
}
public object GetValue(IContext context, ITarget target)
{
return this.parameterValue;
}
public bool Equals(IParameter other)
{
return this.GetType() == other.GetType();
}
}
public class AbstractFactoryModule : NinjectModule
{
private const string ConfigurationScopeName = "ConfigurationScope";
public override void Load()
{
this.Bind<IConfigurationFactory>().To<ConfigurationFactory>();
this.Bind<Configuration>().ToSelf()
.DefinesNamedScope(ConfigurationScopeName);
this.Bind<IFactory>().ToFactory()
.InNamedScope(ConfigurationScopeName);
this.Bind<IProduct1>().To<Product1A>()
.WhenProductFamiliy(ProductFamily.A);
this.Bind<IProduct1>().To<Product1B>()
.WhenProductFamiliy(ProductFamily.B);
}
}
public static class AbstractFactoryBindingExtensions
{
public static IBindingInNamedWithOrOnSyntax<T> WhenProductFamiliy<T>(this IBindingWhenInNamedWithOrOnSyntax<T> binding, ProductFamily productFamily)
{
return binding
.When(x => x.Parameters.OfType<AbstractFactoryConfigurationParameter>().Single().ProductFamily == productFamily);
}
}
使用System.Linq;
使用FluentAssertions;
使用Ninject;
使用Ninject.Activation;
使用Ninject.Extensions.Factory;
使用Ninject.Extensions.NamedScope;
使用Ninject.Modules;
使用Ninject.参数;
使用Ninject.Planning.Targets;
使用Ninject.Syntax;
公共课程
{
私有静态void Main(字符串[]args)
{
var kernel=新的标准内核();
Load();
var configFac=kernel.Get();
//创建与运行时相关的配置
var configA=configFac.CreateConfiguration(ProductFamily.A);
var configB=configFac.CreateConfiguration(ProductFamily.B);
configA.factory.CreateProduct1().Should().BeOfType();
configB.factory.CreateProduct1().Should().BeOfType();
configA.component.factory.Should().Be(configA.factory);
configA.factory.Should().NotBe(configB.factory);
}
}
公共枚举产品系列{A,B}
公共接口IPProduct1{}
公共接口工厂
{
IPProduct1 CreateProduct1();
}
公共类Product1A:IPProduct1{}
公共类Product1B:IPProduct1{}
公共类配置
{
公共只读电子工厂;
公共只读组件;
公共配置(IFactory、组件)
{
这个工厂=工厂;
这个组件=组件;
}
}
公共类组件
{
公共工厂;
公共组件(IFactory工厂){this.factory=factory;}
}
公共接口IConfigurationFactory
{
配置CreateConfiguration(ProductFamily系列);
}
公共类配置工厂:IConfigurationFactory
{
私有只读IResolutionRoot resolutionRoot;
公共配置工厂(IResolutionRoot resolutionRoot)
{
this.resolutionRoot=resolutionRoot;
}
公共配置CreateConfiguration(ProductFamily系列)
{
返回this.resolutionRoot.Get(新的AbstractFactoryConfigurationParameter(family));
}
}
公共类AbstractFactoryConfigurationParameter:IPParameter
{
私有只读ProductFamily参数值;
public AbstractFactoryConfigurationParameter(ProductFamily parameterValue)
{
this.parameterValue=parameterValue;
}
公共产品系列产品系列
{
获取{返回this.parameterValue;}
}
公共字符串名
{
获取{返回this.GetType().Name;}
}
公共图书馆
{
获取{return true;}
}
公共对象GetValue(IContext上下文,ITarget目标)
{
返回此.parameterValue;
}
公共布尔等于(I参数其他)
{
返回此.GetType()==other.GetType();
}
}
公共类AbstractFactoryModule:NinjectModule
{
私有常量字符串ConfigurationScopeName=“ConfigurationScope”;
公共覆盖无效负载()
{
this.Bind().To();
this.Bind().ToSelf()
.DefinesNamedScope(配置范围名称);
this.Bind().ToFactory()
.InNamedScope(配置范围名称);
this.Bind()到()
.WhenProductFamily(ProductFamily.A);
this.Bind()到()
.whenproductfamily(ProductFamily.B);
}
}
公共静态类AbstractFactoryBindingExtensions
{
公共静态IBindingInName在ProductFamily时使用OronSyntax(此IBindingInName在ProductFamily时使用OronSyntax绑定)
{
返回绑定
.When(x=>x.Parameters.OfType().Single().ProductFamily==ProductFamily);
}
}
请注意,我不相信命名范围对于您的用例是必要的。命名范围确保一个类型只有一个实例(此处为
using System.Linq;
using FluentAssertions;
using Ninject;
using Ninject.Activation;
using Ninject.Extensions.Factory;
using Ninject.Extensions.NamedScope;
using Ninject.Modules;
using Ninject.Parameters;
using Ninject.Planning.Targets;
using Ninject.Syntax;
public class Program
{
private static void Main(string[] args)
{
var kernel = new StandardKernel();
kernel.Load<AbstractFactoryModule>();
var configFac = kernel.Get<ConfigurationFactory>();
// create runtime dependent configs
var configA = configFac.CreateConfiguration(ProductFamily.A);
var configB = configFac.CreateConfiguration(ProductFamily.B);
configA.factory.CreateProduct1().Should().BeOfType<Product1A>();
configB.factory.CreateProduct1().Should().BeOfType<Product1B>();
configA.component.factory.Should().Be(configA.factory);
configA.factory.Should().NotBe(configB.factory);
}
}
public enum ProductFamily { A, B }
public interface IProduct1 { }
public interface IFactory
{
IProduct1 CreateProduct1();
}
public class Product1A : IProduct1 { }
public class Product1B : IProduct1 { }
public class Configuration
{
public readonly IFactory factory;
public readonly Component component;
public Configuration(IFactory factory, Component component)
{
this.factory = factory;
this.component = component;
}
}
public class Component
{
public IFactory factory;
public Component(IFactory factory) { this.factory = factory; }
}
public interface IConfigurationFactory
{
Configuration CreateConfiguration(ProductFamily family);
}
public class ConfigurationFactory : IConfigurationFactory
{
private readonly IResolutionRoot resolutionRoot;
public ConfigurationFactory(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public Configuration CreateConfiguration(ProductFamily family)
{
return this.resolutionRoot.Get<Configuration>(new AbstractFactoryConfigurationParameter(family));
}
}
public class AbstractFactoryConfigurationParameter : IParameter
{
private readonly ProductFamily parameterValue;
public AbstractFactoryConfigurationParameter(ProductFamily parameterValue)
{
this.parameterValue = parameterValue;
}
public ProductFamily ProductFamily
{
get { return this.parameterValue; }
}
public string Name
{
get { return this.GetType().Name; }
}
public bool ShouldInherit
{
get { return true; }
}
public object GetValue(IContext context, ITarget target)
{
return this.parameterValue;
}
public bool Equals(IParameter other)
{
return this.GetType() == other.GetType();
}
}
public class AbstractFactoryModule : NinjectModule
{
private const string ConfigurationScopeName = "ConfigurationScope";
public override void Load()
{
this.Bind<IConfigurationFactory>().To<ConfigurationFactory>();
this.Bind<Configuration>().ToSelf()
.DefinesNamedScope(ConfigurationScopeName);
this.Bind<IFactory>().ToFactory()
.InNamedScope(ConfigurationScopeName);
this.Bind<IProduct1>().To<Product1A>()
.WhenProductFamiliy(ProductFamily.A);
this.Bind<IProduct1>().To<Product1B>()
.WhenProductFamiliy(ProductFamily.B);
}
}
public static class AbstractFactoryBindingExtensions
{
public static IBindingInNamedWithOrOnSyntax<T> WhenProductFamiliy<T>(this IBindingWhenInNamedWithOrOnSyntax<T> binding, ProductFamily productFamily)
{
return binding
.When(x => x.Parameters.OfType<AbstractFactoryConfigurationParameter>().Single().ProductFamily == productFamily);
}
}