Закон Деметры на Фабричный Образец и Введение Зависимости - PullRequest
1 голос
/ 27 апреля 2009

У меня есть вопрос относительно внедрения зависимостей.

скажи, что я хочу создать класс позвони, WebGetTask

WebGetTask потребуется зависимость от HttpService

плохой код 1 Код:

private HttpService  httpService;
...   
List<WebGetTask> list = new ArrayList<WebGetTask>();   
for(...)   
{   
   list.add(new WebGetTask(httpService)); 
}
...

ки. я знаю, что это плохо, потому что httpService вводится, но его никогда не использовали, за исключением создания на новом WebGetTask

ки плохой код 2 Код:

private WebGetTaskFactory webGetTaskFactory;
...  
List<WebGetTask> list = new ArrayList<WebGetTask>();  
for(...)  
{   
    list.add(webGetTaskFactory.newTask());  
}  
...

я думаю, что это лучше, потому что мы используем фабрику но... но ..

откуда я стою, я могу увидеть это в WebGetTaskFactory мы все еще внедряем HttpService и не делаем с ним ничего, кроме единственной цели создания нового WebGetTask

так резюмировать мой вопрос Как мне спроектировать фабричный класс (WebGetTaskFactory), который создает новые объекты (WebGetTask), когда новым объектам требуется зависимость (HttpService) от их конструктора без простого внедрения и передачи зависимости (HttpService)? или, скорее, это способ сделать это? если так, то все хорошо, если нет, то, пожалуйста, объясните мне, как правильно использовать DI и фабричный шаблон. спасибо.

Ответы [ 3 ]

8 голосов
/ 27 апреля 2009

Я собираюсь предположить, что код, который вы показали, является частью класса DownloadManager, и что вы вводите свои зависимости через конструктор. В этом случае я ожидаю, что стартовый код, который склеивает все вместе, будет выглядеть так:

IHttpService httpService = new HttpService();
IWebGetTaskFactory webGetTaskFactory = new WebGetTaskFactory(httpService);
IDownloadManager downloadManager = new DownloadManager(webGetTaskFactory);

Класс DownloadManager знает только об интерфейсе IWebGetTaskFactory. Он не знает об IHttpService, поэтому удовлетворяет закону Деметры.

edit: После перечитывания вашего вопроса, вы, похоже, беспокоитесь, что вы не «используете» HttpService на своей фабрике, за исключением того, что передаете его новому WebGetTask. Хорошо. И WebGetTaskFactory, и WebGetTask нуждаются в экземпляре HttpService для выполнения своей работы. Это не нарушение закона Деметры.

3 голосов
/ 27 апреля 2009

Хорошо, в LoD нет ничего особенно плохого в передаче объекта реализации, «плагина» в конструкторе. Что важно, так это то, что интерфейс класса мало что говорит вам о этой реализации.

Если ваш интерфейс к WebGetTask зависит от точной реализации HttpService, , что нарушает закон Деметры.

Хитрость в том, чтобы подумать о сигнатуре интерфейса WebGetTask. Само название говорит о том, что вы не совсем следуете Закону Деметры - или принципу наименьшего знания - потому что вы определяете класс, который (1) определен как специфичный для сети, а (2) вместо этого глагол существительного.

Теперь, ни то, ни другое не обязательно является неправильным, но оба они, если хотите, являются "ОУ-запахами", признаками того, что вы, возможно, не думаете достаточно объектно.

Итак, давайте попробуем «рефакторинг» дизайна. Во-первых, подумайте о GetTask, который не имеет связанной с ним «сети». Затем вы можете либо во время конструирования, либо в более позднее время создать сервисный объект и передать его внутрь. Если это HttpService, это нормально, но пользователю вашего класса не нужна информация о том, что находится под прикрытием.

Второе, давайте сделаем это существительным. Назовите это TaskFactory - ваша интуиция привела вас прямо туда - с помощью ctor, который использует IOService, который я только что изобрел как абстрактный интерфейс, реализованный HttpService.

Теперь у вас есть (это своего рода псевдокод Java / C ++, не волнуйтесь о деталях синтаксиса):

 class Task { ... }
 class TaskFactory {
    public TaskFactory(IOServer svc){...}
    public Task get(){...}
 }

и вы используете его, написав

 TaskFactory fac = new TaskFactory(new HttpService());
 Task tsk = fac.get();

Теперь мы понимаем минимум внутренних функций TaskFactory, службы ввода-вывода и, в этом отношении, задач.

1 голос
/ 27 апреля 2009

Существует два способа DI: первый - конструктор, который полезен, когда вводится только один или два объекта, и метод-установщик (фактически столько сеттеров, сколько необходимо).

Если вы хотите использовать фабричный метод для DI, то в принципе он такой же, как метод на основе конструктора.

Пример 1, для конструктора DI:

list.add( new WebGetTask( httpService ) ) ;

Пример 2, для установщика DI:

WebGetTask webGetTask = new WebGetTask();
webGetTask.setHttpService(httpService);
// set other dependencies
list.add(webGetTask);

Фабричный метод лучше всего подходит для случаев, когда вам нужно использовать более сложную логику при создании объектов, которые могут вести себя по-разному, но иметь одинаковый интерфейс, например LoD. Предположим, что интерфейс DownloadManager реализован динамически на основе заводских параметров.

Пример 3, логика создания, инкапсулированная в фабричный метод:

public static DownloadManager createDownloadManager(HttpService httpService){

    if(null!=httpService){
      WebGetTask webGetTask = new WebGetTask();
      webGetTask.setHttpService(httpService);
      // set other dependencies
      return new DownloadManagerImpl1(webGetTask);
    } else {
      return new DownloadManagerImpl2();
    } // if-else
}
...