Преимущества и недостатки метода chaining и возможность замены всех параметров возврата void самим объектом

меня в основном интересует Java, но я думаю, что это общий вопрос. Недавно я работал с Arquillian framework (ShrinkWrap), который использует много цепочек методов. Другим примером цепочки методов являются методы в StringBuilder, StringBuffer. Есть очевидные преимущества использования этого подхода: снижение многословия является одним из них.

теперь мне было интересно, почему не все методы, которые void возвращаемый параметр реализован как chainable? Должно быть что-то очевидное и объективный недостаток в цепочке. Потому что, если все методы цепные, я все равно могу не использовать его.

Я не прошу изменить существующий код на Java, который может что-то сломать где-то, но объяснение, почему он не использовался, было бы неплохо. Я больше спрашиваю с точки зрения будущего фреймворка (написанного на Java).


Я нашел аналогичный вопрос, но оригинальный asker на самом деле интересно, почему это считается хорошей практикой: метод цепочки-почему это хорошая практика, или нет?


хотя есть некоторые ответы, я все еще не уверен, каковы все преимущества и недостатки цепочки и было бы полезно иметь все пустые методы цепочки.

9 ответов


недостатки

  • в основном это смущает подпись, если что-то возвращает новый например, я не ожидаю, что это также будет метод мутатора. Например, если Вектор имеет шкалу метод тогда, если это возвращение полагаю он возвращает новый вектор, масштабируемый входом, если это не так, то I ожидать его в масштабе внутренне.
  • плюс, конечно, вы получаете проблемы, если класс расширен, где частично через цепочку объект получает приведение к супертипу. Это происходит, когда метод цепочки объявлен в родительском классе, но используется на экземпляре дочернего класса.

преимущества

  • позволяет математическое уравнение код стиля должен быть записан как полные уравнения без необходимости нескольких промежуточных объектов (что приводит к ненужным накладным расходам), например, без метода цепочки векторного тройного перекрестного произведения (как случайное пример) должно быть написано либо как

    MyVector3d tripleCrossProduct=(vector1.multiply(vector2)).multiply(vector3);
    

    который имеет недостаток создания промежуточного объекта, который должен быть создан и собран мусор, или

    MyVector3d tripleCrossProduct=vector1;
    tripleCrossProduct.multiplyLocal(vec2);
    tripleCrossProduct.multiplyLocal(vec3);
    

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

    MyVector3d tripleCrossProduct=vector1.multiplyLocal(vector2).multiplyLocal(vector3);
    

    все это предполагает, что vector1 является жертвенным и никогда не будет использоваться снова

  • и, конечно, очевидная польза; краткость. Даже если ваши операции не связаны в поместье моего примера выше, вы все равно можете избежать ненужных ссылок на объект

    SomeObject someObject=new SomeObject();
    someObject
      .someOperation()
      .someOtherOperation();
    

NB MyVector3d не используется как реальный класс Java, но предполагается, что выполняет перекрестный продукт, когда .multiply() методы называются. .cross() не используется, так что "намерение" яснее для тех, кто не знаком с векторным исчислением
Решение NB Amit было первым ответом на использование многострочного метода цепочки, я включаю его как часть четвертого маркера для полноты


цепочка методов-это способ реализации свободных интерфейсов, независимо от языка программирования. Основное преимущество этого (читаемый код) говорит вам, когда именно его использовать. Если нет особой необходимости в читаемом коде, лучше избегать его использования, если API не предназначен для возврата контекста / объекта в результате вызовов метода.

Шаг 1: свободный интерфейс против команды-запрос API

Fluent интерфейс должен рассматриваться против команды запроса API. К поймите это лучше, позвольте мне написать определение пулевого списка API command-query ниже. Проще говоря, это просто стандартный объектно-ориентированный подход кодирования:

  • метод, который изменяет данные, называется Command. Команда не возвращает значение.
  • метод, который возвращает значение, называется Query. Запрос не изменяет данные.

после команды-запрос API даст вам преимущества например:

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

Шаг 2: свободный интерфейс поверх команды-запрос API

но API command-query существует по какой-то причине, и он действительно читается лучше. Тогда как у нас есть преимущества как fluent interface, так и command-query API?

ответ: интерфейс должен быть реализовано поверх команды-query API (в отличие от замены команды-query API by интерфейс свободно). Подумайте о свободном интерфейсе как фасаде над API командного запроса. И в конце концов, это называется свободно "интерфейс" - это читабельный или удобство интерфейса через стандартный API (command-query).

обычно, после того, как API команд-запросов готов (написан, вероятно, проверен блоком, отполирован легко debug), вы можете написать слой программного обеспечения fluent interface поверх него. Другими словами, fluent interface выполняет свои функции с помощью API command-query. Затем используйте интерфейс fluent (с цепочкой методов) везде, где вы хотите удобство и читаемость. Однако, как только вы хотите понять, что на самом деле происходит (например, при отладке исключения), вы всегда можете копаться в API command-query - старый добрый объектно-ориентированный код.


недостаток я нашел с помощью метода цепочек отладки кода, когда NullPointerException или любое другое Exception происходит. Предположим, у вас есть следующий код:

String test = "TestMethodChain"; test.substring(0,10).charAt(11); //This is just an example

затем вы получите индекс строки вне диапазона: исключение при выполнении выше кода. Когда вы попадаете в ситуации реального времени, и такие вещи происходят, вы получаете, какая ошибка линии произошла, но не какая часть цепного метода вызвала ее. Поэтому его нужно использовать разумно там, где он известен эти данные всегда будут приходить или ошибки обрабатываются должным образом.

Он имеет свои преимущества тоже, как вам не нужно писать несколько строк кода и использовать несколько переменных.

многие фреймворки / инструменты используют это как Dozer, и когда я использовал для отладки кода, мне пришлось просмотреть каждую часть цепочки, чтобы найти, что вызвало ошибку.

надеюсь, что это помогает.


вы можете прочитать о свободный интерфейс Мартина Фаулера

вкратце

  • не цепной метод для черт его в первую очередь потому, что он ломается принцип проектирования разделения ответственности командных запросов (CQRS).
  • улучшить дизайн api, приблизив его к пути бизнеса говорит об этих операциях, думайте о них как о внутренних DSLs
  • попробуйте избежать цепных независимых методов, потому что они загрязняют API и не могут выявить намерение клиента/хранитель код

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

CriteriaCollection().instance()
    .addSelect(Criteria.equalTo(XyzCriteria.COLUMN_1, value1))
    .addSelect(Criteria.equalTo(XyzCriteria.COLUMN_2, value2))
    .addSelect(Criteria.isIn(XyzCriteria.COLUMN_3, values3))
    .addOrder(OrderCriteria.desc(XyzCriteria.Order.COLUMN_1));

в конечном счете это улучшает читаемость кода.


Если вы предпочитаете неизменяемости и функциональное программирование, ты никогда не вернешься void.

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

конечно, есть ситуации, когда это не применимо, но функция, возвращающая void можно рассматривать как подсказку, чтобы попробовать его по-другому.


лично я думаю, что это очень полезный шаблон, но он не должен использоваться везде. Рассмотрим ситуацию, когда у вас есть copyTo(T other) метод. Обычно вы ожидаете, что он ничего не вернет, но если он вернет объект того же типа, какой объект вы ожидаете? Такого рода вопрос может быть снят с документацией, но это все равно неоднозначный метод signiature.

public class MyObject {

    // ... my data

    public MyObject copyTo(MyObject other) {

        //... copy data


        // what should I return?
        return this;
    }
}

Это значительный объем работы. Особенно когда речь идет о наследстве. Взгляните на эта отличная статья о шаблоне builder

имеет смысл реализовать его, когда вы ожидаете, что клиенты будут вызывать несколько методов в одном экземпляре последовательно, например шаблон builder, неизменяемые объекты и т. д. Но на мой взгляд в большинстве случаев это не стоит дополнительных усилий.


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

методы Void бывают двух видов: 1. относительно управления жизненным циклом объекта или атрибутов. 2. имея вывод, который полностью обрабатывается в рамках метода и не должен вызывать никаких изменений в другом месте.

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

но почему вы должны сделать метод, возвращающий Void и почему не экземпляр логического (успех или нет)? Мотивация метода для возврата void (как в сеттере) заключается в том, что метод не имеет побочного эффекта. Возврат true или false может быть побочным эффектом. Void означает, что метод не имеет побочного эффекта, когда метод выполняется так, как он был разработан. Метод void, возвращающий исключение, хорош, потому что исключение не является побочным эффектом нормальной обработки метода.

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

для связывания методов void значимым образом подразумевает, что общий объект, на котором методы работают после каждого шага в состоянии, понятном для метода void, работающего над объектом. Все последующие призывы не могут зависеть от результатов его предшественника или влиять на работу призывов после его собственного призыва. Самый простой способ убедиться в этом-не позволить им изменить внутреннее состояние объекта (например, пример регистратора) или позволить каждому методу изменить другую часть внутреннего состояния объекта.

Void методы, которые будут прикованы в моем мнение только те методы, которые имеют вкус 2 и разделяют какой-то вид обработки. Я бы не связывал пустые методы, касающиеся жизненного цикла класса, потому что каждый метод является изменением другой части всего состояния объекта. Наличие этих методов может меняться со временем с любым изменением дизайна класса,поэтому я не советую связывать эти методы. Кроме того, все исключения, создаваемые первым типом методов void, независимы друг от друга, в то время как вы можете ожидать что исключения, прикованный методы Void вкуса 2 обмен тип обработки есть что-то общее.