Быстрый вопрос по дизайну.
ClassA имеет метод под названием DoSomething (args)
В DoSomething (), прежде чем он сможет что-то сделать, ему необходимо проделать некоторую подготовительную работу с аргументами. Я считаю, что это должно быть инкапсулировано в ClassA (в отличие от выполнения подготовительной работы снаружи и передачи ее внутрь), поскольку больше ничего не должно знать, что эта подготовительная работа требуется для DoSomething.
Тем не менее, это то место, где собственно код подготовительной работы заставляет меня задуматься.
Подготовительная работа в моем конкретном примере заключается в создании списка элементов, удовлетворяющих определенному условию, из args.
Я догадываюсь, что мне нужно создать новый класс ListOfStuff, который принимает аргументы в своем конструкторе, и поместить сюда эту подготовительную работу.
С точки зрения TDD, я считаю, что это правильный выбор. Затем мы можем провести модульное тестирование ListOfStuff, пока наше сердце не будет удовлетворено. Если бы мы поместили подготовительную работу в частный метод ClassA, мы смогли бы протестировать ее только косвенно, путем тестирования DoSomething ().
Но разве это излишество? С тех пор, как я принял подход TDD и DI, я увидел, что количество классов, которые я пишу, увеличилось - стоит ли мне беспокоиться?
Та.
Пример: using System; using System.Runtime.CompilerServices; [сборка: InternalsVisibleTo ("ByBox.BusinessLogic.UnitTests" )] пространство имен ByBox.BusinessLogic {....





Какая самая простая вещь, которая могла бы сработать? Это мантра TDD. Не пытайтесь заглядывать слишком далеко вперед. Если придет время создать вспомогательный класс, вы это узнаете, потому что вы будете выполнять все виды связанной работы с помощью нескольких методов в других классах. А пока продолжайте работу по своему методу. Если это делает метод слишком длинным или громоздким для чтения, извлеките работу в отдельный метод. Этот метод также можно проверить, сколько душе угодно, без необходимости в другом классе.
Я думал, вы тестировали только общедоступные методы? Частные методы модульного тестирования, даже с помощью взлома, открытого фреймворком модульного тестирования, всегда казались мне немного «ненадежными».
Это зависит от вашей проблемной области. Продукт моей компании - это библиотека классов - мы, В самом деле, хотим, чтобы модуль частных или внутренних методов был протестирован.
@Duncan - для модульного тестирования используйте атрибут InternalsVisibleTo. Это очень полезный атрибут. Это позволяет «дружественной» сборке видеть внутренние методы / свойства / элементы другой сборки.
Я не говорил, что вам нужно тестировать метод, если он частный. Я сказал, что вы можете это проверить. Лично я бы не стал, потому что я не зависим от него напрямую, и его тестирование сделало бы мои тесты хрупкими. Преждевременно переводить его в другой класс, я считаю ошибкой.
Хммм, хорошо. Возникает другой вопрос - тестировать ли частные методы или нет - но я уверен, что есть много материала, который я могу наверстать. Мой инстинкт - всегда НЕ тестировать частные методы, поскольку они проверяются косвенно. В противном случае вы потратите нереально много времени на написание тестов.
Дизайн вашей объектной модели следует рассматривать как таковой, а не в контексте вашей стратегии развития. Если сборка ListOFStuff и переход к DoSomething действительно подходят для объектной модели лучше всего, сделайте это, независимо от вашей стратегии развития.
Я думаю, вы немного ответили на свой вопрос, однако, поскольку ListOfStuff упрощает модульное тестирование, это, вероятно, означает, что это также более чистый дизайн.
Надеюсь, это поможет!
Я должен сказать, что ListOfStuff будет создан исключительно в качестве помощника для этого, поэтому не будет присутствовать в любом дизайне объектной модели. Хм. Но мне делать нравится за явный интерфейс для тестирования. Хм. Это может быть один из тех случаев, когда «нет правильного или неправильного ответа»!
Определенно поместите его в новый класс. Это называется разделением интересов. Вы же не хотите перегружать класс и заставлять его делать много чего другого. Это связано с тем, что ваш класс не сможет использоваться где-либо еще, поскольку он настолько специфичен для одной вещи.
Поместите его в класс, а затем используйте этот класс в другом месте. В противном случае вам придется писать это снова и снова в будущем.
Чтобы сделать его расширяемым и иметь возможность передавать всевозможные различные алгоритмы, паттерн проектирования, который вы будете использовать, - это паттерн Стратегия. Но это на будущее ...
В целом я согласен, ЕСЛИ код также используется где-то еще. Здесь не было такого случая. Здесь он используется только в этом одном методе. Создавая новый метод, я это вижу, но создание нового класса на этом этапе было бы излишним.
@tvanfosson - Вы правы, код на данный момент больше нигде не будет использоваться. Я знаком с мантрой «рефакторинг, если используется более одного раза», но в наши дни я также вижу дизайн в гораздо большей степени, чем разделение проблем. Но я «обеспокоен» (!), Я захожу слишком далеко!
@Duncan, но единственный метод, связанный с расчетом, - это текущий. Вам нужно сбалансировать принципы - в этом случае SoC необходимо сбалансировать с YAGNI (вам это не понадобится).
Здесь есть несколько эвристик.
Ну, три эвристики. Никто не ждет испанской инквизиции.
Определенно нет для 1 и 2, большой возможно для 3. Мне действительно нравится, как он инкапсулирует всю эту подготовительную работу в аккуратный класс, но я думаю, что, возможно, я считаю разделение проблем слишком экстремальным. Это была мысль в пятницу днем!
Нет, мне кажется, ты правильно думаешь. У вас может быть аргумент в пользу частного внутреннего класса; в противном случае поместите подготовительную работу в частный метод.
Since adopting the TDD and DI approach, I've seen the number of classes that I write multiply - should I be worried?
Если ваша цель - процедурное программирование, да. Но поскольку вы почти наверняка хотите работать в объектно-ориентированной манере, нет.
Большинство людей используют слишком мало типов, тратят слишком мало времени на размышления об объектно-ориентированном дизайне.
Ваши вопросы о классовой ответственности отражают зрелость мышления (имхо).
SoC действителен только в том случае, если рассматриваемая «проблема» не имеет ничего общего с классом, или вы обнаружите, что классы разделяют эту «проблему» (иначе говоря, нарушая DRY). В вашем случае кажется, что код является внутренним для класса, поэтому, возможно, функция private была бы более подходящей.
Как сказал тванфоссон выше, вам нужно сбалансировать SoC с YAGNI. Лично я думаю, что вы, возможно, обдумываете это преждевременно (я знаю! Я тоже постоянно так делаю).
Да, я думаю, если бы вы выполняли SoC как чисто академическое упражнение, или ListOfStuff делал что-то ДЕЙСТВИТЕЛЬНО сложное, и я хотел бы протестировать это самостоятельно, у меня был бы класс ListOfStuff, который бы выполнял эту работу. Однако с прагматической точки зрения я бы просто поместил всю работу в ClassA. Компромисс!
Для модульного тестирования используйте атрибут InternalsVisibleTo. Это очень полезный атрибут. Это позволяет «дружественной» сборке видеть внутренние методы / свойства / элементы другой сборки.