Что такое fluent-сеттер

    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

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

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

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

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

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


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

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


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


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

            Сеттеры, кто бы что не говорил, но раскрывают инкапсуляцию (как и геттеры, кстати). Что бы понимать — инкапсуляция включает в себя и абстракцию от реализации в том числе. Прокидывание get/set наверх — это как торчащие кишки реализации наружу, когда реализация меняется — придётся менять и эти самые get/set, а так не должно быть. Интерфейс не должен меняться. Это отличет как раз хороший код от плохого.

            Помимо этого можно вспомнить труды, например, Эрика Эванса для понимания принципов предметно-ориентированного программирования. Т.е. DDD. Ну и ещё с SOLID тоже. Принцип Open-Close никто не отменял.

            P.S. Помимо этого стоит отметить, что я в первом своём комментарии упоминал на счёт DTO — это как раз и есть тот самый инструмент (точнее структура данных), которая должна быть абсолютно «тупой», где реализация свойств (т.е. get + set для данных) допускается и желательна, т.к. он и существует для того, чтобы хранить и передавать эти самые данные. С VO чуть сложнее, но по-моему сеттеры там не всегда будут мешать, хотя я бы воздержался, т.к. это всё же часть предметной области.
            +1
            Нда, не стоит так говорить. Если бы изучали не только PHP знали бы чуточку больше. Использование свойств часто вносит неясность, хорошего тоже мало. Особенно когда вылетает исключение в свойстве, очень весело.
              +1
              Ну я наверное криво выразился. Вначале я хотел описать историческую ремарку о появлении самого понятия «геттеров» и «сеттеров». А потом, во втором абзаце, раскрыть мысль почему торчать наружу данными — это плохо.

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

              Нет, я хотел лишь сказать, что раскрывать инкапсуляцию — это плохо, а getSome и setSome — это тоже самое, что и писать «public $field;» внутри классов.
            +12
            Ты молодец, конечно, что открываешь для себя азы программирования. Но, может не стоит по каждому пустяку строчить статью?
              –4
              Не для всех термин fluent setter входит в азы.
                0
                Почитай про fluent interface в программировании. Сеттеры — лишь частный случай.
              –3
              Позже я нашёл статью про Fluent Interface, где мне попался более приятный глазу пример на python.

              Извините, но для питониста подобный код выглядит не приятно, а дико. В Python, во-первых, считается хорошим тоном придерживаться CQS, а чейнинг нарушает CQS. Во-вторых, в Python есть нормальные свойства (@property), поэтому геттерам-сеттерам в нём нет оправданий. Хотя геттеры-сеттеры − это вообще-то антипаттерн для любого языка, даже для Java. Тем более в таком количестве, чтобы их чейнить ради читаемости. Тут проблемы в проектировании, а не в читаемости.
                0
                Спасибо, что указали на ошибку. Конечно вместо «обычные сеттеры» я имел в виду «обычные property».
                  –2
                  Я не хотел вас поправлять в том, как называются разные фичи ЯП. Я имел в виду, что не сто́ит ровнять все ЯП под одну гребёнку. Fluent Interface нерелевантен для Python.
                0
                давно видел такое, по сути билдер. Но то что это называется «fluent setter» впервые слышу, спасибо!
                  0
                  Fluent interface в общем случае (setter — частный случай) — паттерн/парадигма в программировании, которая местами успешно используется. Например, LINQ в дотнете основан на этой идее, а поверх неё даже DSL свой сделан.
                    0

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

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

                      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
                        Но всё же jQuery очень удобен (personal feeling)
                        0
                        Fluent-сеттеры придуманы, чтобы код стал понятнее и чище.
                        спорное утверждение.
                        Если такие последовательности форматировать, то получаем последовательный код, вид в профиль, а если нет, то длинные цепочки, скрывающиеся за правым краем окна редактора.
                        Для тривиальных вызовов такой подход возможно удобен, а стоит замаячить на горизонте ошибкам, и удобство сразу становится не столь очевидным.
                          0
                          Текучий интерфейс (англ. fluent interface) в разработке программного обеспечения — способ реализации объектно-ориентированного API, нацеленный на повышение читабельности исходного кода программы. Название придумано Эриком Эвансом и Мартином Фаулером.

                          © Кто-то в Wiki

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

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

                        Самое читаемое