image

Сегодня в PhpStorm я создал приватную переменную и заметил, что IDE предлагает мне создать два вида сеттера: обычный setter и fluent setter.

Термин «fluent setter» мне раньше не встречался, поэтому я создал оба варианта.

/* PHP */
class Foo {
    private $var;

    // Обычный setter
    public function setVar($var) {
        $this->var = $var;
    }

    // Это fluent setter
    public function setVar($var) {
        $this->var = $var;
        return $this;
    }
}

Ага, значит, fluent setter, — это сеттер, который возвращает сам объект.

Какая глупость. Кому может понадобиться конструкция вида

$object = $object->setVar(1);

// Это же то же самое, что и просто 
$object->setVar(1);

// И даже если создавать новую переменную, польза сомнительная
$sameObject = $object->setVar(1);

Это на первый взгляд.

Чуть позже я догадался. Это то, что называют чейнингом в jQuery. Благодаря возвращённому объекту, мы можем сразу применить следующий сеттер.

/* Javascript */
$('#elementId').val(13).css('font-size', '20px').appendTo(element2);

// И в столбик мне нравится:
$('#elementId')
    .val(13)
    .css('font-size', '20px')
    .appendTo(element2);

Fluent-сеттеры придуманы, чтобы код стал понятнее и чище.

Да, в Javascript это заметно. В Java и PHP — тоже. Тем более, там и так активно используются функции-сеттеры, так почему бы не делать их в fluent-варианте.

/* php */
$car->setColor('#f00')->setWeight('1200')->setPower(55000);

$car
    ->setColor('#f00')
    ->setWeight('1200')
    ->setPower(55000);

в Python, конечно, можно написать функции-сеттеры, но польза от них не очевидная. Мне лично будет удобнее использовать обычный питоновский сеттер, который выглядит не как функция, а как оператор присваивания. Делать же сеттеры вида set_var() в Python, на мой взгляд, противоречит питоновским идеям простоты.

# python

# обычные @property 
car.color = '#f00'
car.weight = 1200
car.power = 55000

# fluent-сеттеры, которые выглядят как функции. Так разве лучше? Лично меня смущает. 
car.color('#f00').weight('1200').power(55000)

# Да и так тоже не лучше...
car.set_color('#f00').set_weight('1200').set_power(55000)

# Если писать в столбик, то надо слэши добавлять, и это ещё страшнее выглядит.
car.set_color('#f00')    .set_weight('1200')    .set_power(55000)

Позже я нашёл статью про Fluent Interface, где мне попался более приятный глазу пример на python.

Минутка самообразования закончена.

Комментарии (21)


  1. SerafimArts
    07.11.2018 13:24
    -6

    Сеттеры — это зло. Это следствие того, что в языках просто нет свойств, так что любое присвоение невозможно переопределить просто так, без лишних километров кода. И пришли они в пхп из мира джавы, да.

    Но само существование сеттеров намекает на анемичность и неполноценность модели предметной области, и использование оных можно одобрить лишь в DTO и ValueObject, в любых остальных случаях рекомендуется «behaviour style», т.е. ориентированность на поведение, желательно вместо с поддержкой ISP.


    1. konshyn
      07.11.2018 14:03

      Что значит сеттеры зло?

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


      1. argentumbolo
        07.11.2018 14:34

        Помимо установления значения функция может выполнять и другие действия, например, высчитывать какие-то внутренние состояния или переменные

        Справедливости ради — обработчик проперти может делать то же самое.


      1. SerafimArts
        07.11.2018 15:46
        +3

        Ну то и значит. Сеттеры — это плохо для любой предметной логики. А если метод выполняет ещё какую-то логику, то тем более.

        Пара примеров
        Вопрос на засыпку:
        $user->setEmail('email@example.com');
        


        Ожидается ли какая-то дополнительная логика в этом методе, кроме почты? Кажется, что нет. Если она есть — это пример плохо спроектированного интерфейса взаимодействия.

        А теперь вот так:
        $user->updateEmail('email@example.com');
        
        // Или лучше даже так, например:
        $transport = new SmtpTransport();
        $user->updateEmail('email@example.com', $transport);
        


        Уже становится понятно, что это обновление почты, помимо установки значения, ещё может поменяться флаг подтверждения почты в false и отправиться письмо поверх нужного транспорта (хотя это уже пример высокой связанности, я бы так не рекомендовал писать всё же).


    1. AxisPod
      07.11.2018 18:51
      +1

      Нда, не стоит так говорить. Если бы изучали не только PHP знали бы чуточку больше. Использование свойств часто вносит неясность, хорошего тоже мало. Особенно когда вылетает исключение в свойстве, очень весело.


      1. SerafimArts
        07.11.2018 18:58
        +1

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

        И только после вашего этого замечания понял, что мой комментарий можно воспринимать как «вот если бы свойства были, то вот прям зажили бы».

        Нет, я хотел лишь сказать, что раскрывать инкапсуляцию — это плохо, а getSome и setSome — это тоже самое, что и писать «public $field;» внутри классов.


  1. KevlarBeaver
    07.11.2018 13:29
    +6

    Ты молодец, конечно, что открываешь для себя азы программирования. Но, может не стоит по каждому пустяку строчить статью?


    1. Survtur Автор
      07.11.2018 13:53
      -3

      Не для всех термин fluent setter входит в азы.


      1. KevlarBeaver
        07.11.2018 14:04

        Почитай про fluent interface в программировании. Сеттеры — лишь частный случай.


  1. Tanner
    07.11.2018 13:44
    -2

    Позже я нашёл статью про Fluent Interface, где мне попался более приятный глазу пример на python.

    Извините, но для питониста подобный код выглядит не приятно, а дико. В Python, во-первых, считается хорошим тоном придерживаться CQS, а чейнинг нарушает CQS. Во-вторых, в Python есть нормальные свойства (@property), поэтому геттерам-сеттерам в нём нет оправданий. Хотя геттеры-сеттеры ? это вообще-то антипаттерн для любого языка, даже для Java. Тем более в таком количестве, чтобы их чейнить ради читаемости. Тут проблемы в проектировании, а не в читаемости.


    1. Survtur Автор
      07.11.2018 13:48

      Спасибо, что указали на ошибку. Конечно вместо «обычные сеттеры» я имел в виду «обычные property».


      1. Tanner
        07.11.2018 14:48

        Я не хотел вас поправлять в том, как называются разные фичи ЯП. Я имел в виду, что не сто?ит ровнять все ЯП под одну гребёнку. Fluent Interface нерелевантен для Python.


  1. MeGaPk
    07.11.2018 14:06

    давно видел такое, по сути билдер. Но то что это называется «fluent setter» впервые слышу, спасибо!


    1. KevlarBeaver
      07.11.2018 14:23

      Fluent interface в общем случае (setter — частный случай) — паттерн/парадигма в программировании, которая местами успешно используется. Например, LINQ в дотнете основан на этой идее, а поверх неё даже DSL свой сделан.


    1. zagayevskiy
      07.11.2018 14:31

      Нет, суть билдера вообще не в этом. Fluent — это сахар. А суть в создании иммутабельного объекта из разрозненных данных.


  1. NickyX3
    07.11.2018 15:33

    Всю жизнь так делаю, а сейчас выяснилось, что это fluent называется.


  1. garex
    07.11.2018 18:34
    +2

    ocramius.github.io/blog/fluent-interfaces-are-evil


    1. break Encapsulation
    2. break Decorators/Composition
    3. harder to Mock
    4. make diffs harder to read
    5. less readable (personal feeling)
    6. cause BC breaks during early development stages


    1. Survtur Автор
      07.11.2018 19:29

      Но всё же jQuery очень удобен (personal feeling)


  1. Zanak
    08.11.2018 14:08

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


    1. Survtur Автор
      08.11.2018 14:18

      Текучий интерфейс (англ. fluent interface) в разработке программного обеспечения — способ реализации объектно-ориентированного API, нацеленный на повышение читабельности исходного кода программы. Название придумано Эриком Эвансом и Мартином Фаулером.

      © Кто-то в Wiki


      1. Zanak
        08.11.2018 17:45

        Из вашего же источника:

        Такой стиль косвенно полезен повышением наглядности и интуитивности кода [источник не указан 2750 дней]. Однако может весьма пагубно сказаться на отладке, если цепочка действует как одно выражение, куда отладчик не всегда может установить промежуточную точку останова.