【问题标题】:Constructor Injection and when to use a Service Locator构造函数注入以及何时使用服务定位器
【发布时间】:2011-01-02 15:49:47
【问题描述】:

我很难理解 StructureMap 的部分用法。 特别是,在文档中声明了一种常见的反模式,即仅使用 StructureMap 作为服务定位器而不是构造函数注入(直接来自 Structuremap 文档的代码示例):

 public ShippingScreenPresenter()
    {
        _service = ObjectFactory.GetInstance<IShippingService>();
        _repository = ObjectFactory.GetInstance<IRepository>();
    }

代替:

public ShippingScreenPresenter(IShippingService service, IRepository repository)
    {
        _service = service;
        _repository = repository;
    }

这对于一个非常短的对象图来说很好,但是当处理很多层次的对象时,这是否意味着您应该从顶部向下传递更深的对象所需的所有依赖项?这肯定会破坏封装并暴露太多关于实现更深层对象的信息。

假设我正在使用 Active Record 模式,因此我的记录需要访问数据存储库才能保存和加载自身。如果此记录加载到一个对象中,该对象是否调用 ObjectFactory.CreateInstance() 并将其传递给活动记录的构造函数?如果该对象在另一个对象内部怎么办。它是否将 IRepository 作为它自己的参数从更远的地方开始?这将向父对象公开我们此时正在访问数据存储库的事实,而外部对象可能不应该知道这一点。

public class OuterClass
{
    public OuterClass(IRepository repository)
    {
        // Why should I know that ThingThatNeedsRecord needs a repository?
        // that smells like exposed implementation to me, especially since
        // ThingThatNeedsRecord doesn't use the repo itself, but passes it 
        // to the record.
        // Also where do I create repository? Have to instantiate it somewhere
        // up the chain of objects
        ThingThatNeedsRecord thing = new ThingThatNeedsRecord(repository);
        thing.GetAnswer("question");
    }
}

public class ThingThatNeedsRecord
{
    public ThingThatNeedsRecord(IRepository repository)
    {
        this.repository = repository;
    }

    public string GetAnswer(string someParam)
    {
        // create activeRecord(s) and process, returning some result
        // part of which contains:
        ActiveRecord record = new ActiveRecord(repository, key);
    }

    private IRepository repository;
}

public class ActiveRecord
{
    public ActiveRecord(IRepository repository)
    {
        this.repository = repository;
    }

    public ActiveRecord(IRepository repository, int primaryKey);
    {
        this.repositry = repository;
        Load(primaryKey);
    }

    public void Save();

    private void Load(int primaryKey)
    {
        this.primaryKey = primaryKey;
        // access the database via the repository and set someData
    }

    private IRepository repository;
    private int primaryKey;
    private string someData;
}

任何想法将不胜感激。

西蒙

编辑: 意见似乎是注入应该从顶层开始。 ActiveRecord 将被注入到注入到 OuterClass 中的 ThingThatNeedsRecord 中。 这样做的问题是,如果 ActiveRecord 需要使用运行时参数(例如要检索的记录的 id)来实例化。如果我在顶部将 ActiveRecord 注入到 ThingThatNeedsRecord 中,我必须以某种方式弄清楚此时需要什么 id(它将顶层暴露给它不应该实现的实现),或者我必须有一个部分构造的 ActiveRecord并稍后设置 Id。如果我需要 N 条记录并且直到在 ThingThatNeedsRecord 中执行逻辑才会知道,这会变得更加复杂。

【问题讨论】:

标签: dependency-injection structuremap constructor-injection service-locator


【解决方案1】:

控制反转就像暴力。如果它不能解决您的问题,则说明您使用的不够多。或something like that

更重要的是,我认为您的OuterClass 应该通过构造函数注入将ThingThatNeedsRecord 注入其中。同样ThingThatNeedsRecord 应该有ActiveRecord 注入它。这不仅可以解决您眼前的问题,还可以使您的代码更加模块化和可测试。

【讨论】:

  • 我认为这可以追溯到我最初的担忧。如果我在 OuterClass 之上创建 ThingThatNeedsRecord 并将 ActiveRecord 注入其中,那么 OuterClass 之上的对象将在对象图中暴露给 ThingThatNeedsRecord 的内部实现。
  • 如果您通过 (StructureMap) 容器创建 OuterClass,则使用 OuterClass 的代码不会暴露于对 ThingThatNeedsRecord 的依赖。正确配置后,容器将提供ThingThatNeedsRecordOuterClass,并递归处理对象图中的任何其他依赖项。
  • 最终,在顶层,您将有一个服务位置调用,您可以从 ObjectFactory 中获取一个对象。但是,该对象的所有依赖项(及其依赖项)都将由容器满足,因此进行服务位置调用的代码不需要了解这些依赖项。
猜你喜欢
  • 2021-12-17
  • 1970-01-01
  • 1970-01-01
  • 2021-09-26
  • 2021-07-07
  • 2019-04-25
  • 2020-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多