#dependency-injection #dependency-management
#внедрение зависимостей #управление зависимостями
Вопрос:
В нашем проекте у нас есть класс KnowledgeBaseManager, который используется другими классами следующим образом:
KnowledgeBaseManager manager = KnowledgeBaseManager.get();
manager.foo();
KnowledgeBaseManager содержит статическую переменную standardKnowledgeBaseManager, которая инициализируется при первом использовании:
class KnowledgeBaseManager {
private static KnowledgeBaseManager standardKnowledgeBaseManager = null;
public static KnowledgeBaseManager get() {
if (standardKnowledgeBaseManager == null) {
standardKnowledgeBaseManager = new KnowledgeBaseManager();
// initialize standardKnowledgeBaseManager with appropriate knowledge base
}
return standardKnowledgeBase;
}
Кроме того, у нас есть параметризованный конструктор
public static KnowledgeBaseManager get(OntModel model) {...}
который мы используем до сих пор только для модульного тестирования, где нам нужен KnowledgeBaseManager с тестовой базой знаний в фоновом режиме.
Теперь у нас есть следующая задача: для разработки мы хотим, чтобы приложение использовало менеджер КБ с другой базой знаний в фоновом режиме (из-за скорости). Чтобы быть более конкретным, мы создаем веб-приложение с помощью Wicket. Итак, мы хотим объявить где-нибудь в начале приложения, какая база знаний и KnowledgeBaseManager используются в приложении (в зависимости от того, находимся ли мы в разработке или развертывании). Код для использования KB-manager (например
KnowledgeBaseManager manager = KnowledgeBaseManager.get();
теперь) не следует менять для этого.
Вопрос в том, какая архитектура лучше всего подходит для этого?
Я думал об использовании инфраструктуры внедрения зависимостей, такой как PicoContainer или Guice, но у меня нет никакого опыта в этом, и я не уверен, что это будет накладно для этой конкретной проблемы. Любые предложения по лучшим практикам в нашем случае?
Ответ №1:
Вы реализуете одноэлементный шаблон — и если ваш код многопоточный, вы реализуете его неправильно (см. http://java.sun.com/developer/technicalArticles/Programming/singletons / )
Более того, как вы обнаружили, синглтоны (т. Е. Глобальные переменные) плохо подходят для тестирования. Внедрение зависимостей — один из ответов. Сначала игнорируйте все фреймворки. Это означает, что вы пишете свой KnowledgeBaseManager традиционным способом — класс с конструктором и методами, без статических однофакторных методов. Код, использующий KnowledgeBaseManager, не создает экземпляр или не выполняет поиск KnowledgeBaseManager — он скорее получает его с помощью конструктора или метода установки (я предпочитаю первый):
public class ClassUsingKnowledgeBaseManager {
protected final KnowledgeBaseManager knowledgeBaseManager;
public ClassUsingKnowledgeBaseManager(KnowledgeBaseManager knowledgeBaseManager) {
this.knowledgeBaseManager = knowledgeBaseManager;
}
// ...
}
Обратите внимание, что, учитывая, что класс не выполняет поиск / создание экземпляра менеджера, ему все равно, используете ли вы синглтон или что-то еще, и что вы можете легко создать экземпляр другого менеджера в своем тесте, никоим образом не касаясь вашего кода.
После того, как вы структурируете свой код таким образом, вы можете обнаружить, что в итоге у вас получаются большие уродливые классы, которые просто создают экземпляры всех необходимых вам объектов, что вам нужен дополнительный код для привязки объектов к разным областям (т. Е. Объектам с областью сеанса или запроса) и для использования конфигурационных файлов для управления созданием экземпляра… если вы обнаружите, что пишете много повторяющегося или шаблонного кода, вы можете проверить DI-фреймворки, которые, вероятно, избавят вас от этого.
Но для многих программ вы могли бы написать код в стиле DI без использования DI-framework.
Ответ №2:
Вы могли бы использовать шаблон конструктивного проектирования.
Вы можете создать фабрику и настроить ее так, чтобы она возвращала нужные вам объекты.