Должен ли я сделать конкретные зависимости класса явным путем внедрения конструктора в мои классы? - PullRequest
9 голосов
/ 26 октября 2010

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

Итак, допустим, у нас есть ClassB, которое будет израсходовано, будет израсходовано ClassA:

alt text

class ClassB {
}

Мой вопрос заключается в том, должен ли я создать ClassB в ClassA или передать эту ответственность "вверх" в иерархии? Я опишу оба случая:

class ClassA {
    private ClassB classB;

    public ClassA() {
        this.classB = new ClassB();
    }
}

или

class ClassA {
    private ClassB classB;

    public ClassA(ClassB classB) {
        this.classB = classB;
    }
}

Я хотел бы услышать о том, как бы вы это сделали, и каково было бы обоснование этого!

Спасибо

Ответы [ 5 ]

3 голосов
/ 26 октября 2010

Некоторые преимущества вашего первого варианта (A создает B):

  1. Пользователь класса A не должен ничего знать о B или о том, как его создать и настроить. Это делает модель программирования проще.
  2. B также можно сделать частным / внутренним, так что пользователям вашей библиотеки не нужно просматривать множество вспомогательных классов, которые им не нужно видеть.
  3. Вы можете изменить способ работы A, чтобы вообще не использовать B, не нарушая код вызова.
  4. Инкапсуляция - никто не может вмешаться в B извне, пока работает A

Некоторые преимущества вашего второго варианта (A зависит от B):

  1. Пользователь класса A имеет полный контроль над объектом B - они могут настроить его по-разному, они могут передать один и тот же объект в несколько экземпляров A, если захотят.
  2. Он открывает двери для различных реализаций, которые должны быть переданы (если B - интерфейс или базовый класс), что имеет большое значение для модульного тестирования или расширяемости.
  3. обновление : если в будущем создание B будет сложнее (например, B имеет некоторые собственные зависимости), то этим можно управлять извне, не внося изменений в A.

Обратите внимание, что также возможно создать решение "лучшее из обоих миров", используя то, что иногда называют "инъекцией зависимости бедного человека":

class ClassA {
    private ClassB classB;

    public ClassA(ClassB classB) {
        this.classB = classB;
    }

    public ClassA() : this(new ClassB()) {

    }
}
3 голосов
/ 26 октября 2010

Зависит от реализации, общего ответа нет. Если A имеет все необходимые данные и знания для инициализации B, то он может инициализировать его.

Если вы не хотите, чтобы A знал, как инициализировать B, или не хотите предоставлять A все данные для инициализации, то вам следует создать его извне.

2 голосов
/ 26 октября 2010

Вам лучше внедрить интерфейс, который реализован ClassB.

class ClassA {
private InterfaceB B;

public ClassA(InterfaceB B) {
    this.B = B;
}
}
class classB: InterfaceB
{
}

Обратите внимание, что InterfaceB может быть обычным классом в качестве базового класса для ваших производных. В этом случае он все еще работает как интерфейс.

1 голос
/ 26 октября 2010

В первом A принимает на себя полную ответственность класса B создания экземпляров. Таким образом, нет ничего, что может пойти не так. A знает B как и все. В случае, если A хочет, чтобы было установлено несколько свойств B, прежде чем вызывать какой-либо метод для него. Это можно сделать, установив их сразу после инициализации.

В последнем случае A не может быть уверен в B.

Теперь выбор, конечно, за вами.

0 голосов
/ 16 августа 2014

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

1) Вы можете использовать инструмент для внедрения зависимостей, чтобы внедрить объект classB. Хотя он не может внедрить различные реализации, потому что вы привязаны к конкретному классу, он может указывать жизненный цикл объектов (singleton, per-request, per-thread) и любые параметры конструктора, используемые для создания экземпляра объекта.

2) Если вы всегда передаете зависимости в конструкторе (инверсия управления), то, возможно, яснее, какие зависимости у каждого класса сразу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...