Подтвердить что ты не робот

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

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

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

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


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


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

4b9b3361

Ответ 1

Недостатки

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

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

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

    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 было первым ответом на использование многострочного метода, я включаю его как часть четвертой точки пули для полноты

Ответ 2

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

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

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

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

После API-интерфейса командной строки вы получите следующие преимущества:

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

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

Но API-интерфейс командного запроса по какой-то причине существует, и он действительно читает лучше. Тогда как у нас есть преимущества как свободного интерфейса, так и API-интерфейса команд?

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

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

Ответ 3

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

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

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

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

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

Надеюсь, что это поможет.

Ответ 4

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

Суммарно

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

Ответ 5

Этот шаблон полезен, когда необходимо выполнить серию обновлений на одном и том же объекте, и для операций обновления не требуется возвращать статус обновления. Например, я использовал этот шаблон в некоторых 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));

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

Ответ 6

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

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

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

Ответ 7

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

public class MyObject {

    // ... my data

    public MyObject copyTo(MyObject other) {

        //... copy data


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

Ответ 8

Это значительная работа. Особенно, когда наследование задействовано. Посмотрите эту замечательную статью о шаблоне строителя

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

Ответ 9

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

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

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

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

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

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

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