Согласование юнит-тестирования с OOD - PullRequest
5 голосов
/ 07 мая 2011

TDD сегодня в моде, и все больше и больше магазинов программного обеспечения переходят на Agile, Scrum и т. Д. Я, конечно, вижу преимущества автоматизированного тестирования, но я также вижу, что TDD противоречит некоторым принципам хорошего объектно-ориентированного проектирования.

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

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

Ответы [ 6 ]

10 голосов
/ 07 мая 2011

Методы и классы, которые вы считаете деталями реализации, на самом деле являются швами , которые представляют оси, вдоль которых вы можете варьировать и комбинировать компоненты в новые созвездия.

Классическая идея инкапсуляции имеет тенденцию быть слишком грубой, потому что когда вы скрываете много движущихся частей, вы также делаете код очень негибким . Это также имеет тенденцию нарушать многие принципы SOLID - наиболее заметно принцип Единой ответственности .

Большинство объектно-ориентированных шаблонов проектирования, как правило, достаточно мелкозернистые и хорошо вписываются в принципы SOLID, что является еще одним показателем того, что правильный объектно-ориентированный дизайн является мелкозернистым.

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

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

Если вы хотите сохранить грубый API-интерфейс для настройки функций по умолчанию, вы все равно можете сделать это, предоставив Facade . См. Этот пост для более подробной информации: Dependency Inject (DI) «дружественная» библиотека

2 голосов
/ 08 мая 2011

Марк Сееманн отлично ответил. «Единицы» в промышленности часто нарушают принцип единой ответственности и затрудняют обслуживание системы из-за всех внутренних жестких зависимостей. TDD хорошо раскрывает такие недостатки. Единицы могут быть построены как блоки Lego для формирования более крупных функциональных блоков, которые фактически выполняют полезную бизнес-логику. Действительно, я вижу, что TDD очень хорошо поддерживает создание хороших ОО-систем.

Не беспокойтесь о том, чтобы так много скрывать. Думайте о private как о значении «вам не разрешен доступ к этому», а не «системе не требуется доступ к этому». Используйте его осторожно - обычно для вещей, которые портят состояние объекта, если к нему обращаются извне в неправильное время.

2 голосов
/ 07 мая 2011

Возможно, мало литературы, потому что это ложная дихотомия?

TDD требует, чтобы вы вставляли в код швы, которые раскрывают детали реализации через интерфейс.

Нет, конструктор или метод для внедрения зависимости не обязательно должны быть частью интерфейса, который использует вызывающий класс:

class Zoo {
    Animal exhibit;
}

interface Animal {
    void walk();
}

class Dog extends Animal {
    DogFood food;

    Dog(DogFood food) {
        this.food = food;
    }
}

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

В приведенном выше примере Zoo не может получить доступ к DogFood, поскольку он получает Dog после того, как он уже былсыт, и Dog не выставляет свою пищу.

1 голос
/ 09 мая 2011

По моему опыту, TDD очень поддерживает принципы объектно-ориентированного проектирования.

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

Внедрение зависимостей связано со слабой связью - если в классе A используется один или несколько объектов из класса b, хороший дизайн сведет к минимуму знания класса A о внутренних элементах класса B. Я полагаю, что это то, на что вы ссылаетесь, когда вы упомянуть «сокрытие информации».

Подумайте, что произойдет, если класс B изменит свою реализацию. Или, более сложный, но все еще распространенный случай: что если вы хотите динамически заменить в разных подклассах B в зависимости от ситуации (например, вы можете использовать шаблон Strategy), но класс, который принимает это решение, не класс А.

По этой причине в Java есть интерфейсы. Вместо того чтобы делать класс A зависимым от класса B, вы делаете его зависимым от интерфейса, который реализует класс B. Затем вы можете заменить любой класс, который реализует этот интерфейс, без изменения кода внутри A.

Это включает, но не ограничивается, заменой поддельных объектов для целей тестирования.

TDD полностью использует Dependency Injection. Также как TDD использует много принципов ОО. Но DI - это принцип ОО, а не принцип TDD.

1 голос
/ 07 мая 2011

Есть несколько вещей, которые можно сделать для интеграции TDD с ООП, в зависимости от рассматриваемого языка. В Java вы можете использовать отражение для тестирования приватной функциональности, и тест можно поместить в один и тот же пакет (предпочтительно в отдельное дерево исходных текстов) для тестирования приватной функциональности пакета. Лично я предпочитаю тестировать функциональность только через публичный API соответствующего кода.

Я не знаю каких-либо «официальных» ресурсов по этому вопросу, хотя знаю, что Дядя Боб много писал по теме TDD и считает его совместимым с его «твердыми» принципами ООП.

0 голосов
/ 29 мая 2011

При использовании внедрения зависимостей построение объекта и управление графом объекта будут обрабатываться на заводе.Объекты создаются с помощью фабрик, поэтому создатель объекта не будет знать о зависимостях созданного класса.

Например, если класс A имеет зависимости B и C, которые передаются через конструктор, B и C будутобеспечивается заводским методом.Объект, создающий A, не будет знать об объектах B и C.

Каркасы внедрения зависимостей, такие как Google Guice и Ninject , могут использоваться для автоматизации создания фабрики.

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