Использование внедрения зависимостей или есть более простые решения?

#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:

Вы могли бы использовать шаблон конструктивного проектирования.

Вы можете создать фабрику и настроить ее так, чтобы она возвращала нужные вам объекты.