Да, SOLID - это очень хороший способ разработки кода, который можно легко протестировать. Как краткий учебник:
S - Принцип единой ответственности: объект должен делать ровно одну вещь и должен быть единственным объектом в кодовой базе, который делает эту одну вещь. Например, возьмите класс домена, скажем, Счет. Класс Invoice должен представлять структуру данных и бизнес-правила счета-фактуры, используемые в системе. Это должен быть единственный класс, который представляет счет в базе кода. Это может быть далее разбито, чтобы сказать, что метод должен иметь одну цель и должен быть единственным методом в кодовой базе, который удовлетворяет эту потребность.
Следуя этому принципу, вы повышаете тестируемость вашего проекта, уменьшая количество тестов, которые вы должны написать, которые тестируют одну и ту же функциональность на разных объектах, и вы также обычно получаете меньшие части функциональности, которые легче тестировать в изоляции.
O - Открытый / закрытый принцип: класс должен быть открыт для расширения, но закрыт для изменения . Как только объект существует и работает правильно, в идеале не нужно возвращаться к этому объекту, чтобы вносить изменения, которые добавляют новые функциональные возможности. Вместо этого объект должен быть расширен либо путем его извлечения, либо путем добавления в него новых или различных реализаций зависимостей, чтобы обеспечить эту новую функциональность. Это позволяет избежать регрессии; Вы можете ввести новые функциональные возможности, когда и где это необходимо, без изменения поведения объекта, так как он уже используется в другом месте.
Придерживаясь этого принципа, вы, как правило, повышаете способность кода переносить «ложные» действия, а также избегаете необходимости переписывать тесты, чтобы предвидеть новое поведение; все существующие тесты для объекта все еще должны работать в невыполненной реализации, в то время как новые тесты для новой функциональности, использующие расширенную реализацию, также должны работать.
L - Принцип замещения Лискова: класс A, зависящий от класса B, должен иметь возможность использовать любой X: B, не зная разницы. В основном это означает, что все, что вы используете в качестве зависимости, должно иметь поведение, аналогичное тому, которое видит зависимый класс. В качестве короткого примера, скажем, у вас есть интерфейс IWriter, который предоставляет запись (строку), которая реализована ConsoleWriter. Теперь вместо этого вам нужно записать файл, поэтому вы создаете FileWriter. При этом вы должны убедиться, что FileWriter может использоваться так же, как ConsoleWriter (это означает, что единственный способ, которым зависимый может взаимодействовать с ним, - это вызов Write (строка)), и, таким образом, дополнительная информация, которая может понадобиться FileWriter для этого. задание (например, путь и файл для записи) должно быть предоставлено откуда-то еще, кроме зависимого.
Это огромно для написания тестируемого кода, потому что дизайн, который соответствует LSP, может иметь «поддельный» объект, заменяющий реальную вещь в любой точке без изменения ожидаемого поведения, позволяя тестировать небольшие куски кода изолированно с уверенностью что система будет работать с подключенными реальными объектами.
I - Принцип сегрегации интерфейса: Интерфейс должен иметь как можно меньше методов, чтобы обеспечить функциональность роли, определенной интерфейсом . Проще говоря, чем меньше интерфейс, тем лучше интерфейс. Это связано с тем, что большой интерфейс имеет больше причин для изменения и вызывает больше изменений в других местах кодовой базы, которые могут не потребоваться.
Приверженность ISP улучшает тестируемость, уменьшая сложность тестируемых систем и зависимостей этих SUT. Если объект, который вы тестируете, зависит от интерфейса IDoThreeThings, который предоставляет DoOne (), DoTwo () и DoThree (), вы должны смоделировать объект, который реализует все три метода, даже если объект использует только метод DoTwo. Но, если объект зависит только от IDoTwo (который предоставляет только DoTwo), вы можете легко смоделировать объект, у которого есть этот единственный метод.
D - Принцип обращения зависимостей: Конкреции и абстракции никогда не должны зависеть от других конкреций, а от абстракций . Этот принцип непосредственно навязывает принцип слабой связи. Объект никогда не должен знать, что такое объект; вместо этого его должно волновать, что делает объект. Таким образом, использование интерфейсов и / или абстрактных базовых классов всегда предпочтительнее, чем использование конкретных реализаций при определении свойств и параметров объекта или метода. Это позволяет вам менять одну реализацию на другую без необходимости изменения использования (если вы также следуете LSP, который идет рука об руку с DIP).
Опять же, это огромно для тестируемости, так как позволяет вам еще раз внедрить фиктивную реализацию зависимости вместо «производственной» реализации в тестируемый объект, при этом все еще тестируя объект в точной форме, которую он будет иметь, пока в производстве. Это ключ к модульному тестированию "в изоляции".