Law of Demeter (Закон Деметры, LOD) с примером на PHP
Этот закон часто используют вместе с темой Tell Don't Ask. Его идея в том, что объект должен обладать ограниченным знанием о других объектах и модулях, взаимодействовать только с теми, кто имеет к нему непосредственное отношение.
Формально правило звучит так, что в клиентском коде вы можете вызывать методы объектов, которые:
- Были переданы как аргументы;
- Были созданы локально;
- Являются глобальными;
- Собственные методы;
Основная задача — создать условия для слабой связанности (loose coupling) между объектами. Но за счёт чего?
Представьте, вы находитесь в магазине, хотите купить товар стоимостью 25$. Вы дадите продавцу 25$ или вы отдадите продавцу свой кошелек, чтобы тот достал 25$? Давайте разберём этот пример
<php declare(strict_types=1); final class Wallet { public function __construct(private Money $balance) { } public function subtract(Money $money): void { $this->balance = $this->balance->subtract($money); } } final class SomePerson { public function __construct( private readonly Wallet $wallet, ) { } public function getWallet(): Wallet { return $this->wallet; } } #1 $somePerson = new SomePerson(new Wallet(Money::USD(100))); $somePerson->getWallet()->subtract(Money::USD(25)); final class SomeAnotherPerson { public function __construct( private readonly Wallet $wallet, ) { } public function subtractMoney(Money $money): void { $this->wallet->subtract($money); } } #2 $someAnotherPerson = new SomeAnotherPerson(new Wallet(Money::USD(100))); $someAnotherPerson->subtractMoney(Money::USD(25));В первом случае мы явно знаем, что у объекта SomePerson есть Wallet, который мы хотим получить, чтобы достать (вычесть) из него какую-то сумму денег. Таким образом мы создаём сильную связь (tight coupling) с объектом кошелька, а также раскрываем особенности внутренней реализации.
Чем же лучше второй вариант?
Когда у SomeAnotherPerson мы вызываем метод subtractMoney, это нам даёт дополнительную гибкость, возможность легко изменить внутреннюю реализацию данного метода, не изменяя другие участки кода. Как бонус появляется information hiding, а также это дело менее проблематично мокать в тестах.
LOD также называют законом "одной точки" (one dot), т.к. ноги растут из Java. В нашем же случае о его применении стоит задуматься при виде более одной стрелочки ->. Обращаю внимание, что речь идёт о методах (или свойствах), которые позволяют получить промежуточный объект, утрируя - о геттерах. То есть две стрелочки в каком нибудь fluent interface (напр. $filter->limit(10)->offset(0)) сюда не относятся.
У закона Деметры тоже есть свои минусы. Одним из них является необходимость создания большого количества методов-адаптеров. Если вы чувствуете, что классы становятся перегруженными - это признак плохого объектно-ориентированного дизайна.
Можно ли нарушать закон Деметры?
Руководствуйтесь здравым смыслом. Если у вас, например, вложенные DTO, которые предназначены для транспортировки объектов, то нет никакого смысла наворачивать что-то подобное сверху. Используйте только там, где это уместно и помните о преимуществах.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.