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

тегизаметки, php, теория программирования




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




Урок 26. Ключевое слово switch в JavaScript
Отслеживание даты в COREL DRAW, или GlobalMacroStorage и Metadata
Урок 38. Глобальная область видимости JavaScript