Как избежать безумства конструктора конструкции зависимостей?
Я нахожу, что мои конструкторы начинают выглядеть так:
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. Я не уверен в других фреймворках, но я предполагаю, что они делают что-то подобное.
Оба способа работают, это вопрос предпочтения.