Внедрение родителей в составные конструкторы с Unity C # - PullRequest
0 голосов
/ 30 января 2019

Я пытаюсь заставить IoC работать с Unity в C # с идеей передачи класса-оболочки / составного класса дочерним элементам.

Класс верхнего уровня, который составляет несколько классов, предоставляет некоторые общие функции, которые составныеклассы требуют доступа к.

Для иллюстрации:

// The top composite class
public class Context : IContext {
  public ISomething SomethingProcessor { get; }
  public IAnother AnotherProcessor { get; }

  public Context(ISomething something, IAnother another) {
    this.SomethingProcessor = something;
    this.AnotherProcessor = processor;
  }

  // A function that individual classes need access to, which itself calls one of the children.
  public string GetCommonData() {
    return this.AnotherProcessor.GetMyData();
  }
}

public class Something : ISomething {
  private _wrapper;
  public Something(IContext context) {
    this._wrapper = context;
  }

  // This class has no knowledge of IAnother, and requests data from the master/top class, which knows where to look for whatever.
  public void Do() {
    Console.WriteLine(_wrapper.GetCommonData());
  }
}

public class Another : IAnother {
  public string GetMyData() {
    return "Foo";
  }
}

Если вы не использовали IoC, это просто, так как конструктор для класса Context становится:

public Context() {
  this.SomethingProcessor = new Processor(this);
  this.AnotherProcessor = new Another();
}

Но когда вы используете IoC, идея «этого» еще не существует, потому что она еще должна быть построена инжектором.Вместо этого у вас есть круговая зависимость.

container.RegisterType<ISomething, Something>();
container.RegisterType<IAnother, Another>();
container.RegisterType<IContext, Context>();

var cxt = container.Resolve<IContext>();  // StackOverflowException

Приведенный выше пример был значительно упрощен для иллюстрации концепции.Я изо всех сил пытаюсь найти способ «наилучшей практики» работы с такого рода структурами, чтобы включить МОК.

Ответы [ 2 ]

0 голосов
/ 30 января 2019

Фабричный шаблон - это способ создания объекта на основе других зависимостей или логических выборов.

Метод Factory: «Определите интерфейс для создания объекта, но пусть классы, которые реализуют интерфейс, решают, какой класс создать экземпляр. Метод Factory позволяет классу отложить создание экземпляров для подклассов» (c) GoF.

Много конструкций .. отсюда и название Factory Pattern

Пример необработанного кода, который можно использовать с DI

public class ContextFactory : IContextFactory {

  _anotherProcessor = anotherProcessor;

 public ContextFactory(IAnotherProcessor anotherProcessor) {
     //you can leverage DI here to get dependancies
 }

 public IContext Create(){
    Context factoryCreatedContext = new Context();

    factoryCreatedContext.SomethingProcessor = new SomethingProcessor(factoryCreatedContext )
    factoryCreatedContext.AnotherProcessor = _anotherProcessor;

    //You can even decide here to use other implementation based on some dependencies. Useful for things like feature flags.. etc.

    return context;
  }

}

.это может быть?- но здесь все еще существует проблема циклических ссылок, и я бы никогда не зафиксировал такой код.

Проблема здесь, вам нужно сконцентрироваться на Инверсия управления этого GetCommonData

Ваш SomethingProcessor не должен полагаться на методы другого класса.Именно здесь Inheritance может использоваться, но Inheritance может стать очень сложной очень быстро.

Лучший путь вперед - это Определить одну вещь , которая нужна обоим или многим другим местам, и сломатьэто в новой зависимости.Вот как вы инвертируете контроль.

СОВЕТ:

Не переусердствуйте Интерфейсы - используйте интерфейсы, где вы думаете, что вы будете работать с полиморфизмом, например, набор различных объектов, которые должны обещать вам, что ониреализовали конкретный метод / свойство.В противном случае вы перешли на использование интерфейсов и увеличиваете сложность.DI не должен использовать интерфейсы, это может быть конкретная реализация.Интерфейсы в репозиториях - хорошее применение, так как вы можете легко переключать базы данных, но фабрики, подобные этим, на самом деле не нужны.

0 голосов
/ 30 января 2019

Я не знаю названия этого паттерна, или даже если это плохая или хорошая практика, но вы можете решить свою проблему «двойного связывания», создав метод связывания «IContext» вместоделая это в конструкторе.

Например,

1) ISomething имеет void BindContext(IContext context) метод

2) Вы реализуете его так:

class Something : ISomething 
{
   IContext _wrapper;

   // ... nothing in constructor

   public void BindContext(IContext context)
   {
        _wrapper = context;
   }
}

3) Удалите внедрение зависимостей IContext в конструкторе Something.

И вы вызываете его из конструктора контекста:

public Context(ISomething something, IAnother another) {
    this.SomethingProcessor = something;
    this.SomethingProcessor.BindContext(this);

    // same for IAnother
}

И вы делаете то же самое для IAnother.Вы можете даже извлечь какой-нибудь общий интерфейс "IBindContext", чтобы сделать ритм более "СУХИМ" (не повторять себя) и заставить IAnother и ISomething наследовать его.

Не проверено, и снова: не уверен, что это лучший способ сделать такой дизайн зависимостей.Я буду рад, если есть другой ответ, который дает современное представление об этом.

...