Использование обратных выражений с большим эффектом! - PullRequest
2 голосов
/ 26 декабря 2008

Когда я создаю методы с возвращаемыми значениями, я обычно пытаюсь настроить их так, чтобы никогда не было случая, когда метод вызывается таким образом, что ему придется возвращать какое-то значение по умолчанию. Когда я начинал, я часто писал методы, которые что-то делали, и либо возвращали то, что они делали, либо, если они ничего не делали, возвращали ноль. Но я ненавижу иметь уродливые if(!null) заявления по всему моему коду,

Я читаю повторное руководство по ruby, которое я читал много месяцев назад, прагматичными программистами, и я замечаю, что они часто возвращают self (ruby's this), когда они обычно ничего не возвращают. Они говорят, что это делается для того, чтобы иметь возможность связывать вызовы методов, как в этом примере с использованием сеттеров, которые возвращают объект, атрибуты которого они устанавливают.

tree.setColor(green).setDecor(gaudy).setPractical(false)

Сначала я нахожу такие вещи привлекательными. Было несколько раз, когда я радовался возможности чередовать вызовы методов, например Player.getHand().getSize(), но это несколько иное, поскольку объект вызова метода меняется от шага к шагу.

Что Stack Overflow думает о возвращаемых значениях? Есть ли какие-то шаблоны или идиомы, которые приходят на ум тепло, когда вы думаете о возвращаемых значениях? Какие-нибудь отличные способы избежать разочарования и увеличить красоту?

Ответы [ 5 ]

5 голосов
/ 26 декабря 2008

По моему скромному мнению, есть три вида возвратов, которые вы должны принять во внимание:

манипулирование свойствами объекта

Первое - это манипулирование свойствами объекта. Шаблон, который вы описываете здесь, очень часто используется при манипулировании объектами. Очень типичный сценарий - использовать его вместе с фабрикой. Рассмотрим этот гипотетический призыв к созданию:

// When the object has manipulative methods:
Pizza p = PizzaFactory().create().addAnchovies().addTomatoes(); 
// When the factory has manipulative methods working on the 
// object, IMHO more elegant from a semantic point of view:
Pizza p = PizzaFactory().create().addAnchovies().addTomatoes().getPizza();

Это позволяет быстро понять, что именно создается или как манипулировать объектом, потому что методы образуют одно читаемое человеком выражение. Это определенно приятно, но не злоупотребляйте. Практическое правило заключается в том, что это можно использовать с методами, возвращаемое значение которых вы также можете объявить как void.

Оценка свойств объекта

Вторым может быть, когда метод оценивает что-либо на объекте. Рассмотрим, например, метод car.getCurrentSpeed(), который можно интерпретировать как сообщение объекту, запрашивающее текущую скорость и возвращающее ее. Это просто вернет значение, не слишком сложное. :)

Заставить объект делать то или иное

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

int new_gear = 20;
if (car.gears.changeGear(new_gear)) // does that mean success or fail?

Здесь вы можете увидеть трудности при разработке метода. Должен ли он вернуть 0 в случае успеха или неудачи? Как насчет -1, если передача не может быть установлена, потому что в машине всего 5 передач? Значит ли это, что текущая передача сейчас тоже равна -1? Метод может вернуть механизм, на который он был изменен, что означает, что вам придется сравнивать аргумент, предоставленный методу, с кодом возврата. Это будет работать. С другой стороны, вы можете просто вернуть либо true, либо false для сбоя, либо false или true для сбоя. Какой из них использовать, можно определить, рассчитав, ожидаете ли вы, что вызовы этих методов скорее не пройдут, либо завершатся успешно.

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

class GearSystem {
// (...)
public:
enum GearChangeResult
{ GearChangeSuccess, NonExistingGear, MechanicalGearProblem };

GearChangeResult changeGear (int gear);
};

Таким образом, для любого программиста, который смотрит на ваш код, становится совершенно очевидным, что означает возвращаемое значение (рассмотрим: if (gears.changeGear(20) == GearSystem::GearChangeSuccess) - гораздо яснее, что это значит, чем в примере выше)

Antipattern: ошибки в качестве кодов возврата.

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

1 голос
/ 26 декабря 2008

Возвращение this также используется в «шаблоне компоновщика», еще одном случае, когда цепочка методов может улучшить читаемость, а также удобство записи.

Нулевое значение часто возвращается в виде внеполосного значения, чтобы указать, что результат не может быть получен. Я считаю, что это совершенно разумно, когда получение результата не является нормальным событием; примеры могут включать в себя нулевое возвращение readLine() в конце файла или нулевое возвращение при предоставлении несуществующего ключа для get(...) метода Map. Чтение до конца файла - это нормальное поведение (в отличие от IOException, которое указывает, что что-то пошло не так, как надо, при попытке чтения). Точно так же поиск ключа и сообщение о том, что он не имеет значения, является нормальным случаем.

Хорошей альтернативой нулю в некоторых случаях является «нулевой объект», который является полноценным экземпляром класса результата, но который имеет подходящее состояние и поведение для случая «никто дома». Например, результатом поиска несуществующего идентификатора пользователя может быть объект NullUser, который имеет имя нулевой длины и не имеет никаких полномочий для действий в системе.

1 голос
/ 26 декабря 2008

Я не согласен, что методы никогда не должны возвращать ноль. Наиболее очевидные примеры из системного программирования. Например, если кто-то просит открыть файл, вы просто должны дать ему нулевое значение, если открытие не удается. Нет вменяемой альтернативы. В других случаях уместно использовать значение NULL, например, метод getNextNode (node), когда вызывается на последнем узле связанного списка. Итак, я думаю, что в этих случаях общим является то, что null представляет «без объекта» (без дескриптора файла или без узла списка), что имеет смысл.

В других случаях метод никогда не должен завершаться сбоем, и существует соответствующее средство исключения. Затем, я думаю, что метод цепочки, как ваш пример, может быть использован с большим эффектом. Я думаю, это немного забавно, что вы, кажется, считаете это нововведением "прагматичных программистов". На самом деле, он датируется Лиспом, если не раньше.

0 голосов
/ 26 декабря 2008

В основном методы, которые логически ничего не возвращают, должны ничего не возвращать - c ++ "void". Для языков, у которых нет такой опции, просто не надо ничего возвращать. Ошибка должна быть указана в исключении. Методы «поиска» являются пограничными - в тех случаях иногда полезно возвращать «специальное» значение, когда то, что вы искали, не найдено.

У Smalltalk есть другая идиома, которая может быть полезна: значение или блок ifNotFound.

0 голосов
/ 26 декабря 2008

Это смущает меня. Языки программирования OO должны использовать точку с запятой Smalltalk:

tree color: green;
     decor: gaudy;
     practical: false.

obj method1; method2. означает «вызов метода 1 для объекта, затем метод 2 для объекта». Этот тип настройки объекта очень распространен.

...