需要新对象图时的依赖注入最佳实践

时间:2017-12-12 11:50:08

标签: c# dependency-injection

我是依赖注入的新手,仍然试图绕过它。据我了解,根据书籍Dependency Injection in .NET的最佳实践表明,对象图是在应用程序启动时在Composition Root创建的,并且在应用程序中不再访问容器。

当用户点击新/打开按钮时会发生什么?

通常我会创建所需对象图部分的新实例(Forest)但是如果我不应该访问容器,我是否应该调用传播对象图的clear方法?我是否以某种方式使用了生命周期管理,并确保目前没有对象图部分的新引用?

编辑:

使用MVVM / WPF的示例

public class Bootstrapper {
    public void Initialize() {
        Container.Register<IMainViewModel, MainViewModel>();
        Container.Register<IForest, Forest>();
        Container.Register<ITrees, Trees>();
        Container.Register<ITree, Tree>();
    }
}

public class MainViewModel : IMainViewModel {
    private IForest _forest;

    public MainViewModel(IForest forest) { _forest = forest; }

    public void New() { Forest.Clear(); }
    public void AddTrees(){ _trees.Add(new Tree()); }
}

public class ViewModel : IViewModel {
    private ITrees _trees;
    private ITree _selectedTree;

    public ViewModel(ITrees trees){ _trees = trees; }

    public void AddTrees() { _trees.Add(new Tree()); }
}

public class Forest : IForest {
    private ITrees _trees;

    public Forest(ITrees trees){ _trees = trees; }

    public void AddTree(ITree tree){ _trees.Add(tree); }
    public void Clear(){ _trees.Clear(); }
}

public class Trees : ITrees {
    public List<ITree> trees = new List<ITree>();

    public void Add(ITree tree){ trees.Add(tree); }
    public void Clear(){ trees.Clear(); }
}

1 个答案:

答案 0 :(得分:1)

  

根据.NET中的Dependency Injection一书,指出对象图是在组合根中的app启动时创建的,并且在应用程序中再次访问容器。

本书没有说明应用程序的对象图只应创建一次,并且它当然不会声明应该从应用程序访问容器。

然而,本书的第1版陈述了以下内容(不是确切的引用):

  • 对象图应尽可能靠近应用程序的入口点(第3.3.1节,第75页)。
  • 对象图配置一次(在应用程序启动时),但在整个应用程序的生命周期内创建(解决)很多次(第3.3.2节,第84页)
  • 向DI容器请求来自其他任何地方的粒度服务,但Composition Root意味着服务定位器反模式。 (第5.4节,第155页)

总结:

  • 对象图总是在Composition Root
  • 中组成
  • 在应用程序启动期间,它们仅配置/注册一次
  • 只要需要新的对象图,就可以编写它。这意味着在应用程序的生命周期内可以多次组合对象图
  • 请求新对象图的代码应该始终成为组合根的一部分,以防止服务定位器反模式。
  

但如果我不应该访问容器,我是否应该调用传播对象图的清晰方法?

这个看似矛盾的答案是:抽象!

为了能够从已构建的组件中懒惰地构造对象图,您可以定义特定的抽象(更精确的抽象工厂),以允许创建该特定服务。此工厂抽象可以在应用程序级别定义,并在组合根目录中实现

例如:

// Defined in application
public interface IForestFactory
{
    IForest Create();
}

// Defined inside the Composition Root
public class ForestFactory : IForestFactory
{
    private readonly Container container;

    public ForestFactory(Container container) {
        this.container = container;
    }

    public IForest Create() {
        return this.container.GetInstance<IForest>();
    }
}

// Inside the Bootstrapper
var factory = new ForestFactory(Container);
Container.Register<IForestFactory>(() => factory);
然而,second edition of the book描述了这些工厂通常是代码气味。通常最好使用Virtual Proxy

,而不是使用工厂

虚拟代理是一个应该懒惰创建的对象的替身。然而,虚拟代理实现与延迟创建的对象相同的抽象。这允许将虚拟代理注入到消费者中,而消费者不必知道虚拟代理的存在。

在您的情况下,这意味着您为IForest创建了一个虚拟代理,例如:

// Defined inside the Composition Root
public class LazyForestProxy : IForest
{
    private readonly Container container;

    public LazyForestProxy(Container container){
        this.container = container;
    }

    // Implement IForest members
    void IForest.Run() {
        var forest = GetInstance();

        // Forward the call to the 'real' forest
        forest.Run();
    }        

    private IForest GetInstance() {
        return this.container.GetInstance<IForest>();
    }
}
相关问题