Когда дело доходит до внедрения зависимостей, шаблон абстрактной фабрики часто используется слишком часто.Это не означает, что это плохой шаблон сам по себе, но во многих случаях есть более подходящие альтернативы шаблону абстрактной фабрики.Это подробно описано в Внедрение зависимостей в .NET, второе издание (глава 6.2), где описано, что:
- Абстрактные фабрики не должны использоваться для создания недолговечных,зависимости с состоянием, поскольку потребитель зависимости должен не обращать внимания на время ее существования;с точки зрения потребителя, концептуально должен быть только один экземпляр услуги.
- Абстрактные фабрики часто являются нарушениями принципа инверсии зависимости (DIP), потому что их дизайн часто не устраивает потребителя, в то время как DIP заявляет: «тезисы принадлежат верхним уровням / уровням политики», что означает, что потребитель абстракции должен диктовать свою форму и определять абстракцию таким образом, который больше всего соответствует его потребностям.Разрешение потребителю зависеть как от фабричной зависимости, так и от производимой ею зависимости усложняет потребителя.
Это означает, что:
- Абстрактные фабрики с методом создания без параметров должны быть предотвращеныпотому что это подразумевает, что зависимость недолговечна, а ее срок жизни контролируется потребителем.Вместо этого абстрактные фабрики должны создаваться для зависимостей, которые концептуально требуют создания данных времени выполнения (предоставляемых потребителем).
- Но даже если фабричный метод содержит параметры, необходимо позаботиться о том, чтобы абстрактныйФабрика действительно требуется.Шаблон Proxy часто (но не всегда) лучше подходит, потому что он позволяет потребителю иметь единственную зависимость вместо зависимости как от фабрики, так и от ее продукта.
Внедрение зависимостей способствует составу классовна пути запуска приложения концепция, называемая книгой Root Composition .Корень компоновки - это местоположение, близкое к точке входа этого приложения (ваш метод Main
), и он знает обо всех других модулях в системе.
Поскольку корень компоновки принимает зависимость от всех других модулей в системеобычно нет смысла потреблять абстрактные фабрики в корне композиции.Например, если вы определили абстракцию IXFactory
для получения IX
зависимостей, но корень композиции является единственным потребителем абстракции IXFactory
, вы отделяете что-то, что не требует разделения: корень композиции по сути знаето любой другой части системы в любом случае.
Это похоже на вашу абстракцию IGeneticAlgorithmFactory
.Кажется, единственным ее потребителем является ваш корень композиции.Если это так, эту абстракцию и ее реализацию можно просто удалить, а код в его методе getInstance
можно просто переместить в класс MainProgram
(который функционирует как ваш корень композиции).
Это сложнодля меня, чтобы понять, требуют ли ваши реализации IIndividual
фабрики (это было по крайней мере 14 лет назад, с тех пор как я реализовал генетический алгоритм в университете), но они больше походят на данные времени выполнения, а не на «реальные» зависимости.Таким образом, фабрика может иметь здесь смысл, хотя и проверяет, должно ли их создание и реализация скрываться за абстракцией.Я мог бы представить, что приложение достаточно слабо связано, когда FastGeneticAlgorithm
создает SmallIndividual
экземпляров напрямую.Это, однако, просто дикая догадка.
Кроме того, лучше всего применять Constructor Injection.Это предотвращает временную связь .Кроме того, воздержитесь от указания зависимостей реализаций в определенных абстракциях, как это делает ваш AbstractGeneticAlgorithm
.Это делает абстракцию Leaky Abstraction (что является нарушением DIP).Вместо этого объявите зависимости, объявив их в качестве аргументов конструктора в реализации (FastGeneticAlgorithm
в вашем случае).
Но даже при наличии IIndividualFactory
ваш код можно упростить, следуя следующим рекомендациям:
// Use interfaces rather than base classes. Prefer Composition over Inheritance.
public interface IGeneticAlgorithm { ... }
public interface IIndividual { ... }
public interface IIndividualFactory {
public IIndividual getInstance();
}
// Implementations
public class FastGeneticAlgorithm implements IGeneticAlgorithm {
private IIndividualFactory individualFactory;
// Use constructor injection to declare the implementation's dependencies
public FastGeneticAlgorithm(IIndividualFactory individualFactory) {
this.individualFactory = individualFactory;
}
}
public class SmallIndividual implements IIndividual { }
public class SmallIndividualFactory implements IIndividualFactory {
public IIndividual getInstance() {
return new SmallIndividual();
}
}
public static class Program {
public static void main(String[] args){
AbstractGeneticAlgorithm algoritm = CreateAlgorithm();
algoritm.makeIndividual();
}
private AbstractGeneticAlgorithm CreateAlgorithm() {
// Build complete object graph inside the Composition Root
return new FastGeneticAlgorithm(new SmallIndividualFactory());
}
}