2011-07-27 344 views
6

用DI方法处理条件子工厂的体面方式是什么? 该场景是注入到条目中的加载器对象取决于该条目的设置。最初我是将IoC容器注入工厂,并使用它来根据命名约定进行解析。但是,我真的很想保持工厂清洁的容器。依赖注入和工厂

工厂被注入到一个类中,该类将加载数据库中的所有设置,然后使用工厂创建一组条目。该设置确定在给定条目内使用哪个装载器。

编辑:更改代码以更好地突出显示实际问题。 问题是,必须同时支持多个数据库管理器,如果情况并非如此,那么它会很简单。数据库管理器类型由为特定条目存储的条目设置确定。

public class Entry : IEntry 
{ 
    private ISomething loader; 

    public Entry(ISomething something) 
    { 
     this.loader = something; 
    } 
} 

public class EntryFactory : IEntryFactory 
{ 
    IEntry BuildEntry(IEntrySetting setting) 
    { 
     //Use setting object to determine which database manager will be used 
    } 
} 

public class EntryManager 
{ 
    public EntryManager(IEntryFactory entryFactory) 
    { 
     var entrySettings = this.settings.Load(); 
     foreach(var setting in entrySettings) 
     { 
      this.entries.Add(entryFactory.BuildEntry(setting)); 
     } 
    } 
} 

我曾考虑具有子工厂,主要工厂注册和解析他们的方式,但我不知道是否有更好的方法。

+1

您有一个运行时输入到依赖关系解析机制,所以我怀疑有一种方法来实现完整的容器独立性。所以把它推到工厂似乎是你能做的最好的。您也可能会发现[Autofac的关系类型](http://code.google.com/p/autofac/wiki/RelationshipTypes)有趣。 –

+0

正如我看到你正在你的入门构造函数中工作,这就是为什么整个DI和工厂人员不工作的问题。 loader.Load(); 让构造函数只包含字段初始化。加载方法是做什么的?它是否用数据填充入口对象?在这种情况下,这不是一个构造函数。工厂必须准备好将加载的数据用于输入,从其他地方获取。 –

回答

0

这取决于你的DI框架允许和你没有指定它。使用Autofac基于委托的注册,我来到以下解决方案。请注意,在这两种情况下,ILoaderFactoryIEntryFactory已被替换为简单的Func<>工厂。

解决方案1,用两个工厂:

public class EntryManager 
{ 
    public EntryManager(Func<ILoader, IEntry> entryFactory, Func<Settings, ILoader> loaderFactory) 
    { 
     var entrySettings = this.settings.Load(); 
     foreach(var setting in entrySettings) 
     { 
      this.entries.Add(entryFactory(loaderFactory(setting))); 
     } 
    } 
} 

private static ILoader SelectLoader(IEntrySetting settings) 
{ 
    // your custom loader selection logic 
} 

var builder = new ContainerBuilder(); 
builder.RegisterType<EntryManager>(); 
builder.RegisterType<Entry>().As<IEntry>(); 
builder.Register((c, p) => SelectLoader(p.TypedAs<IEntrySetting>())); 
IContainer container = builder.Build(); 
container.Resolve<EntryManager>(); 

方案2中,仅使用一个工厂:

public class EntryManager 
{ 
    public EntryManager(Func<IEntrySetting, IEntry> entryFactory) 
    { 
     var entrySettings = this.settings.Load(); 
     foreach(var setting in entrySettings) 
     { 
      this.entries.Add(entryFactory(setting)); 
     } 
    } 
} 

private static ILoader SelectLoader(IEntrySetting settings) 
{ 
    // your custom loader selection logic 
} 

var builder = new ContainerBuilder(); 
builder.RegisterType<EntryManager>(); 
builder.Register((c, p) => new Entry(SelectLoader(p.TypedAs<IEntrySetting>()))).As<IEntry>(); 
IContainer container = builder.Build(); 
container.Resolve<EntryManager>(); 
+0

选择逻辑是我质疑的部分,我使用它来让容器根据设置通过使用工厂中的已命名注册解析来解析类型。问题是将工厂与明确了解容器的工厂区分开来的好方法是什么? – crb04c

+0

Unity也支持自动工厂。 – TrueWill

1

我通常做的是创造我的DI容器包装...像IDependencyResolver ...并注入我的工厂。然后,你可以有一个像StructureMapDependencyResolver这样的实现。我喜欢这比注入容器本身更好,因为这使我可以立即更改DI容器(几乎)。至少我的工厂不需要改变。

public interface IDependencyResolver 
{ 
    T Resolve<T>(); 
} 

public class UnityDependencyResolver : IDependencyResolver 
{ 
    private readonly IUnityContainer _container; 

    public UnityDependencyResolver(IUnityContainer container) 
    { 
     _container = container; 
    } 

    public T Resolve<T>() 
    { 
     return _container.Resolve<T>(); 
    } 
} 

这种方法非常灵活,您可以实现自己的依赖关系解析器并手动注入它们。

public class ManualDependencyResolver : IDependencyResolver 
{ 
    public T Resolve<T>() 
    { 
     if (typeof(T)==typeof(ITransactionRepository)) 
     { 
      return new CheckTransactionRespostory(new DataContext()); 
     } 

     throw new Exception("No dependencies were found for the given type."); 
    } 
} 
+3

这里的难点在于它引入了一点Service Locator模式..这在工厂中可能很好,但如果我需要/想要并隐藏依赖关系,它确实会使切换到无容器变得更加困难。 – crb04c

+0

好点。我不介意工厂的服务定位器。然而,如果你想完全移除容器,你可以实现自己的依赖解析器,这些解析器具有硬编码的类依赖关系,甚至可以通过配置文件进行配置。当我键入这些内容时,我有点畏缩,但是如果容器导致问题,您在技术上可以做到这一点。 –

+0

传递DI容器往往是反模式。容器本身就是一个工厂。你已经减少了使用包装的痛苦,但我不认为这是一个最佳的解决方案。 – TrueWill