Работа с модульным программным обеспечением - это то, чем не все из нас могут быть одарены. Код, который должным образом изолирован и спрятан за абстракцией, кажется единорогом, о котором мы слышали, но не видели. Даже если вы этого не видели, нет никаких аргументов, чтобы не гнаться за этим. Закон Деметры может быть чем-то, что отслеживает вашего похотливого друга.

Это не такая уж свежая идея, поскольку она была опубликована в конце 80-х в Северо-Восточном университете, но она все еще актуальна. Закон Деметры определяет, как объекты взаимодействуют друг с другом. Он говорит, что вы должны разговаривать только с тем объектом, который вам непосредственно известен. Иногда можно услышать об этом как о правиле застенчивого программирования или не разговаривать с незнакомцами.

Закон Деметры в теории

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

Закон Деметры определяет только два требования, но они должны выполняться каждым методом в коде. Один метод может работать только с объектами, которые:

  • передается в качестве аргументов методу
  • значения полей, определенных в этом классе

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

Что значит не разговаривать с незнакомцами?

Просто никакой цепочки методов! Однако упрощение Закона Деметры до краткой версии не очень точное. Тем не менее, должна быть возможность использовать свободный интерфейс, но он должен давать краткий обзор. Чтобы лучше понять это, давайте проверим наш образец.

Мы делаем заявку, которая отражает структуру компании. Сама компания состоит из отделов, которые разделены на подразделения. В каждом Дивизионе может быть несколько Команд с несколькими Участниками. Поскольку это конец года, нам нужно подготовить отчет о текущих расходах на персонал по отделам. Быстрая реализация такой функции умещается в несколько строк. Это даже выглядит очень серьезно, как какой-то очень важный кодекс, за который платит бизнес.

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

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

Во всем проекте, в случае, если мы объединяем объекты в цепочку, это может привести к изменению множества мест. Как избежать цепочки?

Подчинение закону Деметры

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

Разработка кода с учетом Закона Деметры

При написании нового кода мы должны использовать только те объекты, которые доступны напрямую. Это означает, что только те, которые определены в текущем классе, предоставлены в качестве аргументов метода или глобально доступны в приложении. В классе Company мы храним список Department, поэтому мы должны вызывать только методы из этого класса. Как рассчитать стоимость отдела, когда нам нужно знать, сколько стоят члены команды, но мы можем вызывать методы только для самого Department? С точки зрения кода, мы даже не должны знать, что есть команды под отделом. Нам нужно делегировать это.

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

Работа с устаревшим кодом

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

Было бы проще делать маленькие шаги во время рефакторинга. Обратите внимание, что Закон Деметры говорит о том, какой метод должен знать, но не о том, о чем знает сам класс. Мы можем использовать это, чтобы немного изменить правила и начать рефакторинг. Отправной точкой может быть самовызов внутреннего метода, который будет обертывать для нас вложенные вызовы.

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

Кодируйте запахи, которые должны привлечь ваше внимание

Помимо метода извлечения, ограничивающего множество «точек» в коде, есть еще кое-что, на что вам следует обратить внимание.

Проверка типов вложенных объектов

Методы связывания - не единственное, что нарушает закон Деметры. Помните, что закон позволяет вам разговаривать только с предметом, который вы знаете напрямую, поэтому спрашивать любой другой объект о том, что это такое, является нарушением.

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

Бесстыдное задание

Иногда у нас возникает соблазн пойти по пути, надеясь, что никто не заметит наш обман. Вместо объединения методов для получения внутреннего объекта вы можете захотеть присвоить возвращаемое значение переменной.

В присвоении возвращаемого значения нет ничего плохого, но вызывать для него методы - это то, чего стоит стыдиться. Он ничем не отличается от прямого связывания методов, но часто применяется, когда закон Деметры понимается неправильно. Для этого тоже есть конкретный случай.

Прямой возврат стоимости

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

Наследование по закону Деметры

До сих пор мы подробно рассказывали, как работать с методами, но как насчет полей? Второй пункт Закона Деметры гласит, что мы можем работать со значениями из полей текущего класса. В зависимости от того, как вы реализуете классы для наследования, вы можете следовать сильной или слабой реализации закона.

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

Резюме

Закон Деметры - это правило, о котором стоит помнить, но я бы не рекомендовал использовать его как догму. Его выполнение значительно снизит сложность кода и упростит тестирование. Я предпочитаю строгую реализацию закона при разработке основной области, но у нее есть недостаток, заключающийся в резком увеличении количества методов, которые используются объектами. Что касается других частей приложения, мы можем немного расслабиться и быть более прагматичными.

Весь код, упомянутый в этой статье, можно найти в моей учетной записи GitHub. Код, относящийся к исходной статье, можно найти в ветке эта.