2010-06-28 3 views
23

При написании API или объекта многократного использования существует ли какая-либо техническая причина, почему все вызовы методов, возвращающие 'void', должны не просто возвращать 'this' (* this на C++)?Зачем API возвращать 'void'?

Например, используя класс строк, мы можем сделать этот вид вещи:

string input= ...; 
string.Join(input.TrimStart().TrimEnd().Split("|"), "-"); 

, но мы не можем сделать это:

string.Join(input.TrimStart().TrimEnd().Split("|").Reverse(), "-"); 

..because Array.Reverse() возвращает void.

Есть много других примеров, где API имеет много пустот возвращения операций, поэтому код будет выглядеть как:

api.Method1(); 
api.Method2(); 
api.Method3(); 

..но это было бы вполне возможно написать:

api.Method1().Method2().Method3() 

.., если бы дизайнер API допустил это.

Есть ли техническая причина для следования этому маршруту? Или это просто стиль, чтобы указать на изменчивость/новый объект?

(х-исх Stylistic question concerning returning void)


Эпилог

Я принял ответ Luvieere как я думаю, что это лучше всего отражает намерение/дизайн, но, кажется, есть популярные примеры API из там, которые отклоняются от этого:

В C++ cout << setprecision(..) << number << setwidth(..) << othernumber;, похоже, изменяет объект cout, чтобы изменить следующую вставленную датум.

В .NET, Stack.Pop() и Queue.Dequeue() оба возвращают элемент, но также меняют коллекцию.

Подложки для ChrisW и другие для получения подробной информации о фактических эксплуатационных расходах.

+1

Шаблон, который вы описываете, также известен как * метод цепочки * (http://martinfowler.com/dslwip/MethodChaining.html). –

+0

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

+0

Ahhh - у него есть имя! Это кажется вполне читаемым и популярным в таких вещах, как JQuery – JBRWilkinson

ответ

26

Методы, которые возвращают состояние пустоты более четко, что у них есть побочные эффекты. Предполагается, что те, которые возвращают измененный результат, не имеют побочных эффектов, включая изменение исходного ввода. Создание метода return void подразумевает, что он изменяет свой вход или какое-то другое внутреннее состояние API.

+0

В C++ существуют другие способы выразить это - функции-члены-члены, например ..? – JBRWilkinson

+0

@JBRWilkinson Объем вопроса шире, чем C++, он также имеет тег C#. То, что я сказал, предназначалось как практика API-интерфейса для агностиков. – luvieere

+0

Хорошо - точка снята. – JBRWilkinson

19

Если вы имели Reverse() возвращающие string, то это не было бы очевидным для пользователя из API вернулся ли он новую строки или один и тот же-один, обращенный на месте.

string my_string = "hello"; 
string your_string = my_string.reverse(); // is my_string reversed or not? 

Вот почему, например, в Python, list.sort() возвращает None; он отличает местный вид от sorted(my_list).

5

Есть ли техническая причина для следования этому маршруту?

В одном из руководящих принципов дизайна C++ «не платите за функции, которые вы не используете»; возврат this будет иметь небольшое (незначительное) ограничение производительности, поскольку функция, которую многие люди (я, например) не желала бы использовать.

+0

Какова производительность на C++? Разве это не указатель на стек? – JBRWilkinson

+2

JBRWilkinson Где бы ни находился этот указатель, он должен быть указателем вызывающего процесса к моменту его возвращения в вызывающий процесс. На самом деле, используя MSVC, указатель 'this' находится в регистре' ecx', а код возврата находится в регистре 'eax': поэтому штраф за производительность будет дополнительной инструкцией' mov ecx, eax' в конце/каждая подпрограмма. – ChrisW

+2

@JBRWilkinson Дополнительный код операции может быть оптимизирован, если он не используется, только когда вызываемая подпрограмма знает, откуда она вызывается, т. Е. Когда она встроена (но для инкрустации все будет иметь проблемы с производительностью). – ChrisW

0

Кроме того, по причинам, связанным с дизайном, есть также небольшая стоимость (как в скорости, так и в пространстве) для ее возврата.

+0

Можете ли вы дать некоторые особенности? – JBRWilkinson

+1

ChrisW предоставил несколько замечаний в своем ответе. В этом отношении каждый язык/платформа должны быть очень похожи. Например, в C# /. NET компилятору необходимо будет добавить команду возврата IL к сборке asssembly, и компилятор JIT должен в большинстве случаев преобразовать это в инструкцию 'mov eax, ecx', как и компилятор C++ в своем примере. –

4

Технический директор, упомянутый многими другими (что void подчеркивает тот факт, что функция имеет побочный эффект) известен как Command-Query Separation.

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

+0

Статья в Вики кажется немного грязной.Для команд вполне нормально указывать, произошло ли что-то неожиданное. Что-то вроде функции incrementAndReturnValue ведет себя как команда, но возвращает значение, позволяющее вызывающему пользователю узнать, что произошло. Можно было бы передать «ожидаемое» значение, и функция вернет логическое выражение, если все было «как ожидалось», но для абонента проще выполнить само сравнение. – supercat

4

Я бы предположил, что одной из причин может быть простота. Проще говоря, API должен быть как можно более минимальным. Это должно быть ясно со всеми его аспектами, для чего.

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

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

В стороннем API я бы предпочел, чтобы такие вопросы просто не возникали.

Если функция не нужна, чтобы вернуть что-либо, она ничего не должна возвращать.

+3

+1. Было бы достаточно просто написать оболочку привязки метода поверх нее, если требуется удобный синтаксис. Я предпочел бы, что pessimizing всех видов функций, возвращая это/* это, когда нет необходимости в пользу дискуссионной стилистической стенографии. – stinky472

3

Если вы собираетесь свой API для вызова из F #, пожалуйста сделать возвращение void если вы не убеждены в том, что этот конкретный вызов метода будет прикован с другом почти каждый раз, когда он используется.

Если вы не хотите, чтобы ваш API был прост в использовании с F #, вы можете прекратить чтение здесь.

F # является более строгим, чем C# в определенных областях - он хочет, чтобы вы были в явном виде о том, вызываете ли вы метод, чтобы получить значение или чисто для его побочных эффектов. В результате вызов метода для его побочных эффектов, когда этот метод также возвращает значение, становится неудобным, поскольку возвращаемое значение должно быть явно проигнорировано, чтобы избежать ошибок компилятора. Это делает «неудобные интерфейсы» несколько неудобными для использования с F #, который имеет свой собственный превосходный синтаксис для объединения серии вызовов вместе.

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

let add x y = 
    Logger.Log(String.Format("Adding {0} and {1}", x, y)) // #1 
    x + y             // #2 

В F #, потому что строка # 1 является вызов метода, который возвращает значение, и мы ничего не делаем с этим значением, считается, что функция add принимает два значения и возвращает этот экземпляр Logger.Однако строка # 2 не только возвращает значение, оно появляется после того, что F # считает оператором return для функции, эффективно создавая два «возвратных» оператора. Это вызовет ошибку компилятора, и мы должны явно игнорировать возвращаемое значение метода Log, чтобы избежать этой ошибки, так что наш метод add имеет только один оператор возврата.

let add x y = 
    Logger.Log(String.Format("Adding {0} and {1}", x, y)) |> ignore 
    x + y 

Как вы можете догадаться, что делает много «Fluent API» вызовы, которые в основном о побочных эффектах становится несколько расстраивает упражнение в дождеванием много ignore заявлений во всем месте.

Вы можете, конечно, иметь лучшее из обоих миров, и, пожалуйста, как разработчики C#, так и F #, предоставляя как свободный API, так и модуль F # для работы с вашим кодом. Но если вы этого не сделаете, и вы намерены использовать свой API для общественного потребления, , пожалуйста, подумайте дважды, прежде чем возвращать this от каждого метода.

+1

Интересно, как другие языки на самом деле зависят от этого шаблона. Спасибо за отличный ответ! – JBRWilkinson

+0

Это напоминает мне «Носороги и тигры» Йегге, в котором обсуждается (между прочим), если и как виртуальные машины могут поддерживать вызов между несколькими языками. «И они похожи (размахивают руками)« Ооооооооооооооооооооооооооооооооооооооооооо! И ответ: «Вы не можете, это фундаментально, эти языки работают по-другому!» – Ken

Смежные вопросы