97 этюдов для программистов. Опыт ведущих экспертов - Пит Гудлиф
Конечно, если этого окажется недостаточно, удалите вообще все и начните сначала. Такое «рисование по памяти» часто позволяет избавиться от ненужного хлама.
Принцип единственной ответственности
Роберт Мартин (Дядюшка Боб)
Вот один из наиболее фундаментальных принципов качественного проектирования:
Собрать вместе те вещи, которые изменяются по одной и той же причине, и разделить те, которые изменяются по разным причинам.
Этот принцип иначе известен как принцип единственной ответственности, или SRP (single responsibility principle). Если коротко, он гласит, что причина изменения любой подсистемы, класса и даже функции должна быть ровно одна. Классический пример — класс с методами для работы с бизнес-правилами, отчетами и базами данных:
public class Employee {
public Money calculatePay()…
public String reportHours()…
public void save()…
}
Кое-кто из программистов считает, что сочетание этих трех функций в одном классе вполне уместно. В конце концов, классы и должны собирать вместе функции, работающие с одними и теми же переменными. Однако проблема в том, что все три функции изменяются по совершенно разным причинам. Функция calculatePay (рассчитать зарплату) изменяется вместе с бизнес-правилами расчета заплаты. Функция reportHours (отчитаться о часах) изменяется, когда требуется другой формат отчета. Функция save (сохранить) изменяется, когда администратор базы данных меняет схему базы данных. В совокупности эти три причины делают Employee очень неустойчивым классом. Он будет меняться по каждой из этих причин. И что еще важнее, эти изменения затронут любые классы, которые зависят от Employee.
В хорошей архитектуре система разделена на компоненты, которые можно развернуть независимо. Независимость развертывания означает, что изменение одного компонента не требует повторного развертывания других. Но если Employee интенсивно используется многими классами в других компонентах, каждое изменение Employee может требовать повторного развертывания других компонентов, что сводит на нет основное преимущество компонентного подхода к проектированию (или SOA, если вам нравится это более модное название). Следующее простое разделение решает проблему:
public class Employee {
public Money calculatePay()…
}
public class EmployeeReporter {
public String reportHours(Employee e)…
}
public class EmployeeRepository {
public void save(Employee e)…
}
Каждый класс можно поместить в отдельный компонент. Или, выражаясь точнее, все классы, создающие отчеты, поместить в компонент отчетов. Все классы, связанные с базами данных, можно поместить в компонент хранилища. А все бизнес-правила поместить в компонент бизнес-правил.
Внимательный читатель заметит, что в приведенном решении остаются зависимости. Другие классы по-прежнему зависят от Employee. Поэтому если Employee изменится, вполне возможно, что эти классы придется заново скомпилировать и развернуть. Таким образом, Employee невозможно модифицировать, а потом развернуть независимо. Однако другие классы можно модифицировать и независимо развернуть. Никакая их модификация не требует перекомпиляции и повторного развертывания остальных классов. Даже Employee можно независимо развернуть, если тщательно применить принцип инверсии зависимости (DIP, dependency inversion principle), но это уже тема для другой книги.[27]
Продуманное применение принципа единственной ответственности, то есть разделение тех сущностей, которые изменяются по разным причинам, является одной из основ создания архитектур со структурой независимо развертываемых компонентов.
Сначала скажите «да»
Алекс Миллер
Недавно я был в продуктовом магазине и обыскался там эдамаме, зеленых соевых бобов (я лишь приблизительно догадывался, что это какие-то овощи). Я не знал, где мне искать этот продукт: в овощном отделе, в отделе замороженных продуктов или на полках с консервами? Наконец я сдался и обратился за помощью к сотруднице магазина. Она тоже не знала!
Сотрудница магазина могла отреагировать на мою просьбу по-разному. Она могла дать мне понять, что только дурак не знает, где это искать, или отделаться туманными намеками, или даже просто сказать, что у них нет такого товара. Но она посчитала возможным найти решение и помочь покупателю. Позвав других сотрудников, она уже через пару минут отвела меня к нужному товару. Эдамаме оказались в отделе замороженных продуктов.
В данной ситуации сотрудница выслушала просьбу и стала исходить из того, что проблема решится, и просьба будет удовлетворена. Она начала с того, что сказала да вместо нет.
Когда мне впервые пришлось занять должность технического руководителя, мне казалось, что главное в моей работе — защищать мои прекрасные программы от потока нелепых требований, исходившего от менеджеров по продукту и бизнес-аналитиков. В большинстве разговоров я рассматривал любую просьбу как атаку, требующую защитной реакции, а не положительного ответа.
В какой-то момент на меня снизошло прозрение, и я увидел, что возможен другой подход к работе, который отличается от моего всего лишь начальным да вместо начального нет. Я пришел к убеждению, что начинать разговор с положительного ответа — важнейшая часть работы технического руководителя.
Эта простая перемена радикально изменила мой подход к работе. Оказалось, существует много способов говорить. Когда слышишь: «Послушай, это приложение станет в сто раз круче, если мы сделаем все окна круглыми и прозрачными!», можно отвергнуть это предложение как нелепое. Но обычно лучше сначала спросить: «А почему?». Часто существует реальный и убедительный довод, почему этот человек требует круглые прозрачные окна. Например, ведутся переговоры с новым крупным клиентом, у которого комиссия по стандартизации требует эти самые круглые прозрачные окна.
Обычно, ознакомившись с контекстом просьбы, обнаруживаешь, что открываются новые возможности. Зачастую просьба может быть выполнена в рамках существующего продукта каким-то другим способом, что позволяет ответить да, нисколько не напрягаясь: «Собственно говоря, вы можете загрузить в пользовательских настройках «шкурку» (skin) с круглыми прозрачными окнами и активизировать ее».
Иногда у людей появляются идеи, которые кажутся несовместимыми с вашим видением продукта. Я обнаружил, что в этом случае обычно полезно обратиться с вопросом «почему» к самому себе. Порой сама попытка объяснить себе причину помогает понять ошибочность первой реакции. Если это не так, то можно облегчить ситуацию, включив в обсуждение других ответственных лиц. Помните, что цель всего этого — сказать да другому человеку и попытаться сделать так, чтобы все получилось, и не только ради него, но ради себя и всей своей команды.
Если вы сможете убедительно объяснить, почему предложенная функция