Внедрение конструктора и когда использовать локатор сервисов

Я изо всех сил пытаюсь понять части использования 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 необходимо создать с параметром времени выполнения (например, идентификатором записи, которую нужно получить). Если я ввожу ActiveRecord в ThingThatNeedsRecord прямо вверху, мне нужно каким-то образом выяснить, какой идентификатор должен быть в этой точке (что открывает верхний уровень для реализации, чего не должно), или мне нужно иметь частично построенный ActiveRecord и установите идентификатор позже. Это усложняется, если мне нужно N записей и я не узнаю, пока не выполнит логику внутри ThingThatNeedsRecord.


person Simon    schedule 02.01.2011    source источник
comment
Возможный дубликат: stackoverflow.com/ questions / 4570750 /   -  person Mark Seemann    schedule 02.01.2011


Ответы (1)


Инверсия контроля похожа на насилие. Если это не решает вашу проблему, вы используете его недостаточно. Или что-нибудь в этом роде.

Более того, я думаю, что ваш OuterClass должен быть ThingThatNeedsRecord введен в него через инъекцию конструктора. Точно так же ThingThatNeedsRecord должно было быть введено ActiveRecord. Это не только решит вашу непосредственную проблему, но также сделает ваш код более модульным и тестируемым.

person Daniel Pratt    schedule 02.01.2011
comment
Я думаю, это восходит к моей первоначальной озабоченности. Если я создаю ThingThatNeedsRecord над OuterClass и внедряю в него ActiveRecord, то объект над OuterClass становится доступным для внутренних реализаций ThingThatNeedsRecord в графе объектов. - person Simon; 02.01.2011
comment
Если вы создаете OuterClass через контейнер (StructureMap), то код, который потребляет OuterClass, не подвергается зависимости от ThingThatNeedsRecord. Правильно настроенный контейнер будет предоставлять от ThingThatNeedsRecord до OuterClass, а также рекурсивно обрабатывать любые другие зависимости в графе объектов. - person Daniel Pratt; 02.01.2011
comment
В конечном счете, на верхнем уровне у вас БУДЕТ вызов определения местоположения службы, при котором вы получаете объект из ObjectFactory. Однако все зависимости этого объекта (и их зависимости) будут удовлетворяться контейнером, поэтому код, который выполняет вызов определения местоположения службы, не должен знать об этих зависимостях. - person Joshua Flanagan; 02.01.2011