Как избежать безумства конструктора конструкции зависимостей?

Я нахожу, что мои конструкторы начинают выглядеть так:

public MyClass(Container con, SomeClass1 obj1, SomeClass2, obj2.... )

с постоянно увеличивающимся списком параметров. Поскольку "Контейнер" является контейнером для инъекций, почему бы просто не сделать это:

public MyClass(Container con)

для каждого класса? Каковы недостатки? Если я это сделаю, мне кажется, что я использую прославленную статику. Пожалуйста, поделитесь своими мыслями о безумии безумства в IoC и Dependency.

Ответ 1

Вы правы, что если вы используете контейнер в качестве локатора сервисов, он более или менее прославил статический factory. По многим причинам Я считаю это анти-шаблоном.

Одним из замечательных преимуществ Injection Constructor является то, что он делает очевидным очевидное нарушение принципа

Ответ 2

Я не думаю, что у ваших конструкторов классов должна быть ссылка на ваш период контейнера IOC. Это представляет собой ненужную зависимость между вашим классом и контейнером (тип зависимости, который МОК пытается избежать!).

Ответ 3

Трудность прохождения параметров не является проблемой. Проблема в том, что ваш класс делает слишком много и должен быть разбит больше.

Injection Dependency может действовать как раннее предупреждение для слишком больших классов, особенно из-за возрастающей боли при передаче во всех зависимостях.

Ответ 4

Я столкнулся с аналогичным вопросом о зависимой от конструктора Injection и о том, насколько сложным он передавался во всех зависимостях.

Один из подходов, который я использовал в прошлом, - использовать шаблон фасада приложения с использованием уровня сервиса. У этого был бы грубый API. Если эта услуга зависит от репозиториев, она будет использовать инсталляцию setter для частных свойств. Для этого требуется создание абстрактного factory и перемещение логики создания репозиториев в factory.

Подробный код с объяснением можно найти здесь

Рекомендации по IoC в сложном уровне обслуживания

Ответ 5

Это подход, который я использую

public class Hero
{

    [Inject]
    private IInventory Inventory { get; set; }

    [Inject]
    private IArmour Armour { get; set; }

    [Inject]
    protected IWeapon Weapon { get; set; }

    [Inject]
    private IAction Jump { get; set; }

    [Inject]
    private IInstanceProvider InstanceProvider { get; set; }


}

Вот грубый подход, как выполнить инъекции и запустить конструктор после инъекции значений. Это полностью функциональная программа.

public class InjectAttribute : Attribute
{

}


public class TestClass
{
    [Inject]
    private SomeDependency sd { get; set; }

    public TestClass()
    {
        Console.WriteLine("ctor");
        Console.WriteLine(sd);
    }
}

public class SomeDependency
{

}


class Program
{
    static void Main(string[] args)
    {
        object tc = FormatterServices.GetUninitializedObject(typeof(TestClass));

        // Get all properties with inject tag
        List<PropertyInfo> pi = typeof(TestClass)
            .GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
            .Where(info => info.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0).ToList();

        // We now happen to know there only one dependency so we take a shortcut just for the sake of this example and just set value to it without inspecting it
        pi[0].SetValue(tc, new SomeDependency(), null);


        // Find the right constructor and Invoke it. 
        ConstructorInfo ci = typeof(TestClass).GetConstructors()[0];
        ci.Invoke(tc, null);

    }
}

В настоящее время я работаю над хобби-проектом, который работает так https://github.com/Jokine/ToolProject/tree/Core

Ответ 6

Проблема:

1) Конструктор со все возрастающим списком параметров.

2) Если класс наследуется (Ex: RepositoryBase), то смена конструктора подпись вызывает изменение в производных классах.

Решение 1

Передать IoC Container в конструктор

Почему

  • Больше не увеличивается список параметров
  • Конструкторская подпись становится простой

Почему бы не

  • Делает ваш класс тесно связанным с контейнером IoC. (Это вызывает проблемы, когда 1. вы хотите использовать этот класс в других проектах, где вы используете другой контейнер IoC. 2. вы решили изменить контейнер IoC)
  • Делает класс менее понятным. (Вы действительно не можете посмотреть на конструктор классов и сказать, что ему нужно для функционирования.)
  • Класс может получить доступ ко всем возможностям.

Решение 2

Создайте класс, который группирует всю службу и передает ее конструктору

 public abstract class EFRepositoryBase 
 {
    public class Dependency
    {
        public DbContext DbContext { get; }
        public IAuditFactory AuditFactory { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
        }
    }

    protected readonly DbContext DbContext;        
    protected readonly IJobariaAuditFactory auditFactory;

    protected EFRepositoryBase(Dependency dependency)
    {
        DbContext = dependency.DbContext;
        auditFactory= dependency.JobariaAuditFactory;
    }
  }

Производный класс

  public class ApplicationEfRepository : EFRepositoryBase      
  {
     public new class Dependency : EFRepositoryBase.Dependency
     {
         public IConcreteDependency ConcreteDependency { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory,
            IConcreteDependency concreteDependency)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
            ConcreteDependency = concreteDependency;
        }
     }

      IConcreteDependency _concreteDependency;

      public ApplicationEfRepository(
          Dependency dependency)
          : base(dependency)
      { 
        _concreteDependency = dependency.ConcreteDependency;
      }
   }

Почему

  • Добавление новой зависимости к классу не влияет на производные классы
  • Класс агностик контейнера IoC
  • Класс описателен (в аспекте его зависимостей). По соглашению, если вы хотите знать, какой класс A зависит от того, какая информация накапливается в A.Dependency
  • Конструкторская подпись становится простой

Почему бы не

  • необходимо создать дополнительный класс
  • регистрация услуг становится сложной (вам необходимо регистрировать каждый X.Dependency отдельно)
  • Концептуально такой же, как передача IoC Container
  • ..

Решение 2 является просто сырым, хотя, если есть твердый аргумент против него, тогда будет оценен описательный комментарий

Ответ 7

Какую платформу внедрения зависимостей вы используете? Вы пробовали использовать инъекцию на основе сеттера?

Преимущество внедрения на основе конструктора заключается в том, что он выглядит естественным для программистов на Java, которые не используют рамки DI. Вам нужно 5 вещей для инициализации класса, тогда у вас есть 5 аргументов для вашего конструктора. Недостатком является то, что вы заметили, оно становится громоздким, когда у вас много зависимостей.

С помощью Spring вы можете передать требуемые значения с помощью сеттеров вместо этого, и вы могли бы использовать @required аннотации, чтобы обеспечить, чтобы они были введены. Недостатком является то, что вам нужно переместить код инициализации из конструктора в другой метод и вызвать вызов Spring, после того как все зависимости будут введены, пометив его с помощью @PostConstruct. Я не уверен в других фреймворках, но я предполагаю, что они делают что-то подобное.

Оба способа работают, это вопрос предпочтения.