构造函数注入以及何时使用服务定位器

时间:2011-01-02 15:49:47

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

我很难理解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将注入到ThingThatNeedsRecord中,并注入到OuterClass中。 这样的问题是,如果ActiveRecord需要使用运行时参数(例如要检索的记录的id)进行实例化。如果我在顶部注入ActiveRecord到ThingThatNeedsRecord,我不得不弄清楚在那一点需要什么id(它将顶层暴露给实现它不应该)或者我必须有一个部分构造的ActiveRecord并稍后设置ID。如果我需要N条记录,并且在ThingThatNeedsRecord中执行逻辑之前不会知道,这会变得更加复杂。

1 个答案:

答案 0 :(得分:6)

控制倒置就像暴力一样。如果它没有解决你的问题,你就没有使用它。或something like that

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