Чувствуете ли вы себя игроком в Дженгу (игра, в которой игроки попеременно добавляют по одному элементу к возводимой конструкции, проигрывает тот, чье действие приведет к её разрушению), когда программируете? Насколько легко вносить изменения в ваши программные системы? Опасаетесь ли вы, что после внесения изменений ваш код неожиданно перестанет работать? Если вы утвердительно ответили на один из этих вопросов — это явный признак некачественно спроектированного кода, который приводит к замедлению вашей работу уже на следующий день после его появления. А приходилось ли вам когда нибудь разрабатывать код, который сложно было понять уже на следующий день? Мне посчастливилось присутствовать на многих презентациях Боба Мартина (Uncle Bob) и мне очень нравится та часть его выступления, где он спрашивает слушателей, сталкивались ли они с существенным замедлением скорости разработки из-за плохого кода. Когда большинство слушателей поднимали руки, он спрашивал: «Так зачем же вы его так написали?».

Поиграем в «Эрудит»?
Эта статья написана по результатам презентации подходов к разработке расширяемого программного кода Марком Ниджхофом(Mark Nijhof) и переведена Алексеем Смирновым с разрешения и благословения автора. В ней на примерах проиллюстрировано использование принципов SOLID, понимание которых будет полезно как новичкам, так и опытным разработчикам.

Выглядит более привычно?
Для того, чтобы решить проблему негибкого кода, нам необходимо изменить стиль, который мы используем для его написания, нужно также больше думать о предстоящих изменениях и писать не только легко модифицируемый код, но и код, который буквально ожидает, что в него будут вноситься изменения в будущем.
Пишите код так, как будто собираетесь изменять его уже завтра. Огромную помощь в написании такого гибкого кода, оказывают паттерны (шаблоны) в которые включены принципы, помогающие в его последующем реструктурировании. В частности — это так называемые SOLID принципы. Давайте попробуем разобраться, что это такое. SOLID — это аббревиатура, которая описывает пять различных принципов, жизненно важных для структурирования кода, значительно улучшающего способности вашей системы справиться с будущими изменениями.

Великолепный дизайн это услышать ответ на сложный вопрос, а потом подумать: Ну конечно же!
Майкл Физер (Michael Feathers ), автор известной книги «Working Effectively with Legacy Code” был один из предложивших аббревиатуру SOLID людей. Давайте рассмотрим, из каких принципов она состоит.
• S — Принцип Персональной Ответственности (Single Responsibility Principle — SRP)
• O — Принцип Открытия — Закрытия (Open Closed Principle — OCP)
• L — Принцип Подстановки Лескоу (Liskov Substitution Principle — LSP)
• I — Принцип Отделения Интерфейса (Interface Segregation Principle — ISP)
• D — Принцип Инверсии Зависимостей (Dependency Inversion Principle — DIP)
Боб Мартин непоколебим на счет последовательности этих принципов, которые не отражает аббревиатура SOLID сама по себе, и предлагает изменить их последовательность на SDOLI. Его предложению мы и последуем.
Цитата Тима Барча (Tim Barcz)
Это моя последовательность
В подтверждение предложения Боба, могу сказать, что я провел несколько внутренних презентаций и каждый раз сталкивался с затруднениями в их объяснении. Мне кажется, что из-за того, что принципы SOLID тесно взаимосвязаны друг с другом и последовательность SDOLI значительно упростит мое повествование. Итак приступим.
Обратите внимание на прекрасный инструмент, которым можно делать все, пока не сломается одно из его лезвий, и тогда придется его выбросить, так как одно сломанное лезвие затрагивает все остальные. Для изменения класса не должно быть более одной причины.
Принцип Персональной Ответственности (Single Responsibility Principle) – это первый принцип в SOLID и он нам сообщает что: “Не должно быть более, чем одной причины для изменения класса”. Понимая за что отвечает тот или иной класс или метод, а что находится вне зоны его ответственности всегда проще их модифицировать. Наличие персональной зоны ответственности практически не оставляет вам шансов сделать изменения, которые могут затронуть другие части системы. Безусловно, вы можете, изменить поведение системы, но не код и это значительно повышает возможности его повторного использования. Более того, программные конструкции с четко выделенной персональной зоной ответственности проще тестировать в изоляции от всего остального.
Давайте внимательно рассмотрим различные зоны ответственности класса OrderProcessor. Их здесь целых три, относящиеся к обработке заказа, его сохранению и отсылке подтверждающего сообщения. Поэтому у нас появляются, как минимум, три причины, чтобы модифицировать этот класс.
Как вы видите, мы разделили зоны ответственности по трем различным классам. Теперь беглого знакомства с этими классами достаточно, чтобы понять, за что они отвечают, что не будет лишним при их повторном использовании.
Не так болезненно, как выглядит. Совсем не так. Низкоуровневые модуле не должны зависеть от низкоуровневых, оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали реализации должны зависеть от абстракций
Принцип Инверсии Зависимостей (Dependency Inversion Principle) это пятый и последний SOLID принцип, который гласит: “Высокоуровневые модули не должны зависеть от низкоуровневых модулей, но оба этих уровня должны зависеть от абстракции” и “Абстракции не должны зависеть от деталей, детали должны зависеть от абстракций”. Бррр. Проще говоря, это означает, что ничто в вашем коде не должно зависеть от конкретной реализации, допустима зависимость только от интерфейсов или абстрактных классов.
Причина, по которой вы должны следовать этому принципу, заключается в том, что он позволяет вам легко заменить одну реализацию на другую, без каких-либо последствий для вашего кода. Да, именно так. Вашему коду абсолютно не должно быть до этого никакого дела.

В данном примере вы можете заметить, что мой класс OrderProcessor зависит от деталей реализации Repository и MailSender. Что если мне придется изменить его реализацию отсылки нотификаций и использовать для этого другой класс, например SmsSender? Плохо то, что из-за того, что класс OrderProcessor наделен знаниями о способе отсылки сообщений, мне придется поменять его реализацию.

Теперь OrderProcessor не имеет больше зависимостей от способа реализации и зависит только от интерфейсов IRepository и IMailSender. Поэтому, если мне потребуется изменить способ отсылки нотификации, я просто создам новую реализацию IMailSender интерфейса и предоставлю ее OrderProcessor’у. Сам же OrderProcessor продолжит работать так же, как и раньше, его реализация не изменится и он продолжит заботиться только о логике своего поведения.

Для парикмахера она потеряна, но вы можете дать ей тушь или помаду, чтобы сделать модификации возможными.
Открытый — Закрытый Принцип второй в нашем списке (Open Closed Principle), он гласит, что: “Программные сущности должны быть открыты к расширению, но закрыты для модификации”. Это означает, что вы должны иметь возможность изменять внешнее поведение и внешние зависимости класса без его физической модификации. Подобное поведение вам потребуется, когда вы работаете в рамках уже определенных интерфейсов и захотите внести определенные изменения в одну из частей кода, оставив без изменений другие его части.

Как вы уже, наверное, успели заметить, этот класс – результат использования Принципа Инверсии Зависимостей (Dependency Inversion Principle), он также придерживается Принципа Открытия Закрытия (Open Closed Principle). Означает ли это, что Принцип Открытия Закрытия может быть достигнут использованием Инверсии Зависимостей (Dependency Inversion)? Конечно, но это далеко не единственный способ сделать класс расширяемым.

На предыдущем слайде я добавил интерфейс IOrderProcessor в декларацию класса OrderProcessor, благодаря этому я смог создать альтернативную реализацию этого класса. Затем я создал класс декоратор (Decorator, шаблон OOD) DecoratedOrderProcessor, который добавляет дополнительное поведение к моему оригинальному классу и избавляет от необходимости его модификации. Используя подобный подход, я даже могу добавить это поведение на лету (at run-time). Другим хорошим примером для демонстрации подобного подхода является конструкция, работающая в FubuMVC.

Мусоровозу безразлично, какой цвет у контейнера с мусором, пока он открываются сверху, а не снизу.
Третий SOLID принцип – это Принцип Замены Лескоу (Liskov Substitution Principle), который нам говорит: “Функция, которая использует ссылку или указатель на базовый класс, должна иметь возможность использовать объект или его производные без знания деталей его реализации”. А это означает, что когда ваш код использует определенный класс или интерфейс, он должен уметь использовать также и производный класс или альтернативную реализацию этого интерфейса без изменения их внутренней реализации. Этот принцип минимизирует влияние, которое модификации оказывают на ваш код.

Обратите внимание на то, как класс PriorityOrder нарушает Принцип Подстановки Лескоу (Liskov Substitution Principle). Когда код, использующий экземпляр класса Order, проверяет допустимость состояния заказа (функция IsValid), он ожидает получить результат в виде булево значение, но, когда используется экземпляр класса PriorityOrder, требуется дополнительно обработать потенциальную исключительную ситуацию, которая может возникнуть, когда заказ находится в недопустимом состоянии. Для того чтобы работать с этими классами разработчику необходимо точно знать, с каким именно типом он имеет дело: Order или PriorityOrder. И это, безусловно, нарушает Принцип Подстановки Лескоу.

Это пример использует Боб Мартин. Обратите внимание, что класс Square (Квадрат), предлагает абсолютно отличную от класса Rectangle (Четырехугольник) реализацию определения высоты (Height), и следовательно все клиенты Rectangle, которые рассчитывают на то, что площадь фигуры (Surface) рассчитывается как произведение Width и Height, получат неприятный сюрприз.

Рассказывай доктору только то, что поможет ему вас вылечить, хотя в зависимости от доктора может потребоваться пересказать ему всю свою жизнь. Клиенты не должны зависеть от интерфейсов, которые они не используют
Принцип Отделения Интерфейса (Interface Segregation Principle) это четвертый SOLID принцип и звучит он так: “Клиенты не должны зависеть от интерфейсов, которые они не используют”. Это означает, что когда один объект имеет различные способы использования, он не должен вводить в замешательство различными зонами ответственности своих клиентов, а, стало быть, необходимы различные интерфейсы для отделения видимости различных зон. Я не хочу повторяться, сообщая, что это также минимизирует влияние изменений на ваш код.

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

На примере можно увидеть, что мы создали отдельные интерфейсы для различных шагов процесса оформления заказа. Класс Order все еще выглядит так же, но теперь различные клиенты имеют доступ только к тем функциям класса, которые необходимы им для выполнения их задач. Другими словами, они видят класс Order по разному. Подобный подход позволяет вам создавать и использовать различные представления объекта для каждого из шагов, реализующие только необходимый в данном месте интерфейс.

Программистская магическая шляпа
Делать Инверсию Зависимостей (Dependency Inversion) без Контейнера Инверсии Управления (Inversion of Control Сontainer) сложно. Проблема заключается в том, что все ваши зависимости от конкретной реализации на уровне реализации методов, перекочуют на более высокий уровень кода. Вот именно в этом случае Контейнер Инверсии Управления (Inversion of Control container) приходит нам на помощь.

Посмотрите на этот код. Как видите, зависимости от конкретной реализации еще живы на этом уровне, бррр…

Это выглядит лучше, я бы даже сказал много лучше. Я хочу продемонстрировать вам на этом примере использование общего локатора сервисов (Common Service Locator), который обладает общим интерфейсом и поддерживает все Контейнеры Инверсии Управления (Inversion of Control Containers). Правильный Контейнер Инверсии Управления (Inversion of Control Container) может автоматически разрешать зависимости, которые имеют запрашиваемые классы, и внедрять их во вновь созданные. Другими словами, он делает автоматически то, что я обычно делаю вручную.

Детали конфигурирования будут зависеть от того Контейнера Инверсии Управления (Inversion of Control Container), который вы будете использовать в своем проекте. Я, например, использую StructureMap. Он неплох т.к. конфигурируется в одном месте кода, где определяются все действительные зависимости, и это единственное место в коде, где я знаю о различных реализациях. Многие контейнеры поддерживают конфигурирование при помощи XML, но я противник их использования, так как при этом теряется безопасность типов (type safety) и поддержка рефакторинга.

Разработка при помощи тестов (Test Driven Development / TDD и Behaviour Driven Development / BDD ) или Тест Дривен Дизайн (Test Driven Design) — это техники, которые заставляют вас разрабатывать правильный дизайн. Написание тестов перед написанием кода действительно помогает уделять больше внимания дизайну и концентрироваться на том, что вы собрались реализовывать, так что “потерянное” на разработку тестов время, сторицей, окупится лучшим дизайном, который вы получите в результате. Протестированный код также будет проще модифицировать в будущем. Я не вижу оправдания тому, чтобы не тестировать код. Тестирование используется во многих областях, почему бы не использовать его и при разработке программ?

Не повторяйся (Don’t Repeat Yourself). Как я говорил раньше, если у вас есть похожий функционал, повторенный несколько раз в коде, вы рискуете при его модификации забыть что-либо изменить. И это относится не только к дублированию кода системы, но и к тестам. Дублирование делает ваш код хрупким. Не повторяйтесь! Ну вот, опять я это сделал…

Вам это не понадобится. Добавлю картинку только тогда, когда требования скажут мне это сделать
Вам это не понадобится (You Ain’t Gonne Need It). Такое случается очень часто. Вы просто работаете над какой-то частью вашей системы и думаете: «О! Я придумал штуку, которая непременно мне пригодится. Я добавлю её сейчас, потому, что именно сегодня это сделать проще всего, ведь это именно та часть системы, которой я сегодня занимаюсь». Естественно, когда вы приблизитесь к моменту, когда эта самая штука вам действительно понадобится, у вас будет намного лучшее понимание того, что же вам действительно было нужно. И конечно вы поймете, что это далеко не то, что вы написали ранее. Таким образом вместо экономии времени, вы потратите дополнительное время на переделывание своей работы. Не забывайте, что код написанный на будущее может также содержать дефекты, его необходимо будет сопровождать. Добавить функциональность в грамотно спроектированную систему не составит труда, так что ожидайте изменения правильно!

Всегда пишите код так как будто парень которые его сопровождает неистовый маньяк
Будьте честны с собой и начинайте писать качественный код уже сегодня.
Алексей Смирнов выражает благодарность Сергею Слепченко, 21csm и всем кто помог/поможет с переводом и исправлением неточностей в статье.

Поиграем в «Эрудит»?
Эта статья написана по результатам презентации подходов к разработке расширяемого программного кода Марком Ниджхофом(Mark Nijhof) и переведена Алексеем Смирновым с разрешения и благословения автора. В ней на примерах проиллюстрировано использование принципов SOLID, понимание которых будет полезно как новичкам, так и опытным разработчикам.

Выглядит более привычно?
Для того, чтобы решить проблему негибкого кода, нам необходимо изменить стиль, который мы используем для его написания, нужно также больше думать о предстоящих изменениях и писать не только легко модифицируемый код, но и код, который буквально ожидает, что в него будут вноситься изменения в будущем.
Пишите код так, как будто собираетесь изменять его уже завтра. Огромную помощь в написании такого гибкого кода, оказывают паттерны (шаблоны) в которые включены принципы, помогающие в его последующем реструктурировании. В частности — это так называемые SOLID принципы. Давайте попробуем разобраться, что это такое. SOLID — это аббревиатура, которая описывает пять различных принципов, жизненно важных для структурирования кода, значительно улучшающего способности вашей системы справиться с будущими изменениями.

Великолепный дизайн это услышать ответ на сложный вопрос, а потом подумать: Ну конечно же!
Майкл Физер (Michael Feathers ), автор известной книги «Working Effectively with Legacy Code” был один из предложивших аббревиатуру SOLID людей. Давайте рассмотрим, из каких принципов она состоит.
• S — Принцип Персональной Ответственности (Single Responsibility Principle — SRP)
• O — Принцип Открытия — Закрытия (Open Closed Principle — OCP)
• L — Принцип Подстановки Лескоу (Liskov Substitution Principle — LSP)
• I — Принцип Отделения Интерфейса (Interface Segregation Principle — ISP)
• D — Принцип Инверсии Зависимостей (Dependency Inversion Principle — DIP)
Боб Мартин непоколебим на счет последовательности этих принципов, которые не отражает аббревиатура SOLID сама по себе, и предлагает изменить их последовательность на SDOLI. Его предложению мы и последуем.

Цитата Тима Барча (Tim Barcz)

Это моя последовательность
В подтверждение предложения Боба, могу сказать, что я провел несколько внутренних презентаций и каждый раз сталкивался с затруднениями в их объяснении. Мне кажется, что из-за того, что принципы SOLID тесно взаимосвязаны друг с другом и последовательность SDOLI значительно упростит мое повествование. Итак приступим.

Обратите внимание на прекрасный инструмент, которым можно делать все, пока не сломается одно из его лезвий, и тогда придется его выбросить, так как одно сломанное лезвие затрагивает все остальные. Для изменения класса не должно быть более одной причины.
Принцип Персональной Ответственности (Single Responsibility Principle) – это первый принцип в SOLID и он нам сообщает что: “Не должно быть более, чем одной причины для изменения класса”. Понимая за что отвечает тот или иной класс или метод, а что находится вне зоны его ответственности всегда проще их модифицировать. Наличие персональной зоны ответственности практически не оставляет вам шансов сделать изменения, которые могут затронуть другие части системы. Безусловно, вы можете, изменить поведение системы, но не код и это значительно повышает возможности его повторного использования. Более того, программные конструкции с четко выделенной персональной зоной ответственности проще тестировать в изоляции от всего остального.

Давайте внимательно рассмотрим различные зоны ответственности класса OrderProcessor. Их здесь целых три, относящиеся к обработке заказа, его сохранению и отсылке подтверждающего сообщения. Поэтому у нас появляются, как минимум, три причины, чтобы модифицировать этот класс.

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

Не так болезненно, как выглядит. Совсем не так. Низкоуровневые модуле не должны зависеть от низкоуровневых, оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали реализации должны зависеть от абстракций
Принцип Инверсии Зависимостей (Dependency Inversion Principle) это пятый и последний SOLID принцип, который гласит: “Высокоуровневые модули не должны зависеть от низкоуровневых модулей, но оба этих уровня должны зависеть от абстракции” и “Абстракции не должны зависеть от деталей, детали должны зависеть от абстракций”. Бррр. Проще говоря, это означает, что ничто в вашем коде не должно зависеть от конкретной реализации, допустима зависимость только от интерфейсов или абстрактных классов.
Причина, по которой вы должны следовать этому принципу, заключается в том, что он позволяет вам легко заменить одну реализацию на другую, без каких-либо последствий для вашего кода. Да, именно так. Вашему коду абсолютно не должно быть до этого никакого дела.

В данном примере вы можете заметить, что мой класс OrderProcessor зависит от деталей реализации Repository и MailSender. Что если мне придется изменить его реализацию отсылки нотификаций и использовать для этого другой класс, например SmsSender? Плохо то, что из-за того, что класс OrderProcessor наделен знаниями о способе отсылки сообщений, мне придется поменять его реализацию.

Теперь OrderProcessor не имеет больше зависимостей от способа реализации и зависит только от интерфейсов IRepository и IMailSender. Поэтому, если мне потребуется изменить способ отсылки нотификации, я просто создам новую реализацию IMailSender интерфейса и предоставлю ее OrderProcessor’у. Сам же OrderProcessor продолжит работать так же, как и раньше, его реализация не изменится и он продолжит заботиться только о логике своего поведения.

Для парикмахера она потеряна, но вы можете дать ей тушь или помаду, чтобы сделать модификации возможными.
Открытый — Закрытый Принцип второй в нашем списке (Open Closed Principle), он гласит, что: “Программные сущности должны быть открыты к расширению, но закрыты для модификации”. Это означает, что вы должны иметь возможность изменять внешнее поведение и внешние зависимости класса без его физической модификации. Подобное поведение вам потребуется, когда вы работаете в рамках уже определенных интерфейсов и захотите внести определенные изменения в одну из частей кода, оставив без изменений другие его части.

Как вы уже, наверное, успели заметить, этот класс – результат использования Принципа Инверсии Зависимостей (Dependency Inversion Principle), он также придерживается Принципа Открытия Закрытия (Open Closed Principle). Означает ли это, что Принцип Открытия Закрытия может быть достигнут использованием Инверсии Зависимостей (Dependency Inversion)? Конечно, но это далеко не единственный способ сделать класс расширяемым.

На предыдущем слайде я добавил интерфейс IOrderProcessor в декларацию класса OrderProcessor, благодаря этому я смог создать альтернативную реализацию этого класса. Затем я создал класс декоратор (Decorator, шаблон OOD) DecoratedOrderProcessor, который добавляет дополнительное поведение к моему оригинальному классу и избавляет от необходимости его модификации. Используя подобный подход, я даже могу добавить это поведение на лету (at run-time). Другим хорошим примером для демонстрации подобного подхода является конструкция, работающая в FubuMVC.

Мусоровозу безразлично, какой цвет у контейнера с мусором, пока он открываются сверху, а не снизу.
Третий SOLID принцип – это Принцип Замены Лескоу (Liskov Substitution Principle), который нам говорит: “Функция, которая использует ссылку или указатель на базовый класс, должна иметь возможность использовать объект или его производные без знания деталей его реализации”. А это означает, что когда ваш код использует определенный класс или интерфейс, он должен уметь использовать также и производный класс или альтернативную реализацию этого интерфейса без изменения их внутренней реализации. Этот принцип минимизирует влияние, которое модификации оказывают на ваш код.

Обратите внимание на то, как класс PriorityOrder нарушает Принцип Подстановки Лескоу (Liskov Substitution Principle). Когда код, использующий экземпляр класса Order, проверяет допустимость состояния заказа (функция IsValid), он ожидает получить результат в виде булево значение, но, когда используется экземпляр класса PriorityOrder, требуется дополнительно обработать потенциальную исключительную ситуацию, которая может возникнуть, когда заказ находится в недопустимом состоянии. Для того чтобы работать с этими классами разработчику необходимо точно знать, с каким именно типом он имеет дело: Order или PriorityOrder. И это, безусловно, нарушает Принцип Подстановки Лескоу.

Это пример использует Боб Мартин. Обратите внимание, что класс Square (Квадрат), предлагает абсолютно отличную от класса Rectangle (Четырехугольник) реализацию определения высоты (Height), и следовательно все клиенты Rectangle, которые рассчитывают на то, что площадь фигуры (Surface) рассчитывается как произведение Width и Height, получат неприятный сюрприз.

Рассказывай доктору только то, что поможет ему вас вылечить, хотя в зависимости от доктора может потребоваться пересказать ему всю свою жизнь. Клиенты не должны зависеть от интерфейсов, которые они не используют
Принцип Отделения Интерфейса (Interface Segregation Principle) это четвертый SOLID принцип и звучит он так: “Клиенты не должны зависеть от интерфейсов, которые они не используют”. Это означает, что когда один объект имеет различные способы использования, он не должен вводить в замешательство различными зонами ответственности своих клиентов, а, стало быть, необходимы различные интерфейсы для отделения видимости различных зон. Я не хочу повторяться, сообщая, что это также минимизирует влияние изменений на ваш код.

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

На примере можно увидеть, что мы создали отдельные интерфейсы для различных шагов процесса оформления заказа. Класс Order все еще выглядит так же, но теперь различные клиенты имеют доступ только к тем функциям класса, которые необходимы им для выполнения их задач. Другими словами, они видят класс Order по разному. Подобный подход позволяет вам создавать и использовать различные представления объекта для каждого из шагов, реализующие только необходимый в данном месте интерфейс.

Программистская магическая шляпа
Делать Инверсию Зависимостей (Dependency Inversion) без Контейнера Инверсии Управления (Inversion of Control Сontainer) сложно. Проблема заключается в том, что все ваши зависимости от конкретной реализации на уровне реализации методов, перекочуют на более высокий уровень кода. Вот именно в этом случае Контейнер Инверсии Управления (Inversion of Control container) приходит нам на помощь.

Посмотрите на этот код. Как видите, зависимости от конкретной реализации еще живы на этом уровне, бррр…

Это выглядит лучше, я бы даже сказал много лучше. Я хочу продемонстрировать вам на этом примере использование общего локатора сервисов (Common Service Locator), который обладает общим интерфейсом и поддерживает все Контейнеры Инверсии Управления (Inversion of Control Containers). Правильный Контейнер Инверсии Управления (Inversion of Control Container) может автоматически разрешать зависимости, которые имеют запрашиваемые классы, и внедрять их во вновь созданные. Другими словами, он делает автоматически то, что я обычно делаю вручную.

Детали конфигурирования будут зависеть от того Контейнера Инверсии Управления (Inversion of Control Container), который вы будете использовать в своем проекте. Я, например, использую StructureMap. Он неплох т.к. конфигурируется в одном месте кода, где определяются все действительные зависимости, и это единственное место в коде, где я знаю о различных реализациях. Многие контейнеры поддерживают конфигурирование при помощи XML, но я противник их использования, так как при этом теряется безопасность типов (type safety) и поддержка рефакторинга.

Разработка при помощи тестов (Test Driven Development / TDD и Behaviour Driven Development / BDD ) или Тест Дривен Дизайн (Test Driven Design) — это техники, которые заставляют вас разрабатывать правильный дизайн. Написание тестов перед написанием кода действительно помогает уделять больше внимания дизайну и концентрироваться на том, что вы собрались реализовывать, так что “потерянное” на разработку тестов время, сторицей, окупится лучшим дизайном, который вы получите в результате. Протестированный код также будет проще модифицировать в будущем. Я не вижу оправдания тому, чтобы не тестировать код. Тестирование используется во многих областях, почему бы не использовать его и при разработке программ?

Не повторяйся (Don’t Repeat Yourself). Как я говорил раньше, если у вас есть похожий функционал, повторенный несколько раз в коде, вы рискуете при его модификации забыть что-либо изменить. И это относится не только к дублированию кода системы, но и к тестам. Дублирование делает ваш код хрупким. Не повторяйтесь! Ну вот, опять я это сделал…

Вам это не понадобится. Добавлю картинку только тогда, когда требования скажут мне это сделать
Вам это не понадобится (You Ain’t Gonne Need It). Такое случается очень часто. Вы просто работаете над какой-то частью вашей системы и думаете: «О! Я придумал штуку, которая непременно мне пригодится. Я добавлю её сейчас, потому, что именно сегодня это сделать проще всего, ведь это именно та часть системы, которой я сегодня занимаюсь». Естественно, когда вы приблизитесь к моменту, когда эта самая штука вам действительно понадобится, у вас будет намного лучшее понимание того, что же вам действительно было нужно. И конечно вы поймете, что это далеко не то, что вы написали ранее. Таким образом вместо экономии времени, вы потратите дополнительное время на переделывание своей работы. Не забывайте, что код написанный на будущее может также содержать дефекты, его необходимо будет сопровождать. Добавить функциональность в грамотно спроектированную систему не составит труда, так что ожидайте изменения правильно!

Всегда пишите код так как будто парень которые его сопровождает неистовый маньяк
Будьте честны с собой и начинайте писать качественный код уже сегодня.
Алексей Смирнов выражает благодарность Сергею Слепченко, 21csm и всем кто помог/поможет с переводом и исправлением неточностей в статье.