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 - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
Программы на заказ
Отзывы
Контакты