Почему все не любят геттеры и сеттеры?

Почему все не любят геттеры и сеттеры? Что в них плохого?

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

Но в случае программирования остальных полноценных бизнес-объектов смысловые претензии появляются.

Суть в том, что вместо поштучного заполнения полей через глупые сеттеры и считывания из геттеров для последующих проверок:

$order->setStatus(Order::PAID);
$order->setPayPrice($price);
$order->setPayDate($date);

if ($order->getStatus() === Order::PAID) { ... }

в "настоящих" объектах предполагается создавать полноценные "по-человечески" названные умные бизнес-методы с этой же логикой внутри:

$order->pay($date, $price);

if ($order->isPaid()) { ... }

В этом случае при вызове одного метода pay мы точно не забудем присвоить дату и не сможем присвоить неправильный статус, как мы могли бы что-то перепутать с сеттерами.

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

И при работе с "настоящими" методами нам совершенно не важно, какие у этого объекта имеются приватные поля.

Именно создание таких полноценных умных объектов со всей логикой работы внутри описывает настоящее ООП своим принципом инкапсуляции. Это я рассказывал у себя в статье про структуры и объекты.

В этом и разница. Программист на геттерах и сеттерах программирует объекты просто как наборы отдельных полей:

class Order {
    __construct()

    setDate($date)
    getDate()

    setItems($items)
    getItems()

    setStatus($status)
    getStatus()

    setPayPrice($price)
    getPayPrice()

    setPayDate($date)
    getPayDate()

    setCancelDate($date)
    getCancelDate()

    setCancelReason($reason)
    getCancelReason()
}

по которым непонятно, кто и как это всё присваивает и проверяет.

А "настоящий" ООП-программист без сеттеров и без геттера getStatus() делает полноценные объекты с бизнес-логикой:

class Order {
    __construct($date, $items)
    pay($date, $price)
    cancel($date, $reason)
    isPaid()
    isCancelled()
}

Здесь всё понятно с первого взгляда. И на эти методы с логикой пишет Unit-тесты.

Но может возникнуть вопрос, как вывести эти заказы на странице. Нам, наверное, всё равно понадобятся остальные геттеры вроде getDate() и getItems() для доставания всех данных.

Если мы для рендера на странице будем доставать из БД эти же объекты, то да, такие геттеры понадобятся.

Но если для отображения на странице вместо использования этих объектов мы будем ходить в БД напрямую через голый SQL или QueryBuilder, возвращающий массив или DTO со всеми данными, то тогда и геттеры нам не понадобятся.

Тогда для операций с записью в БД у нас будет доменная бизнес-модель записи из полноценных сущностей:

class Order {
    __construct($date, $items)
    pay($date, $price)
    cancel($date, $reason)
}

А для чтения будет отдельная модель чтения с набором DTO с нужными в разных местах данными. Например, для вывода списка заказов нужны только:

struct OrderRow {
    $id
    $date
    $cost
    $isPaid
    $isCancelled
}

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

Источник

Дмитрий Елисеев
elisdn.ru
Комментарии (1)
Evgenii

Я так понимаю это вы рассказали в контексте последнего видео из серии «ООП: Взаимодействие объектов»? А планируется ли это видео, т.к. стикер «скоро» уже довольно давно висит? :(

Ответить
Зарегистрируйтесь или войдите чтобы оставить комментарий

Или войти через:

Yandex
MailRu
GitHub
Google