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


21

Учебное пособие (для Javascript), которое я делаю, предлагает написать такую ​​функцию:

function sayHello() {
   //Some comments explaining the next line
   window.alert("Hello");
}

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


14
Вероятно, он просто пытается показать вам, как определить свои собственные функции.
Доваль

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

32
Преимущество заключается в том , что она завершает функцию с параметрами. Таким образом, если вы позже захотите сделать свое приложение испанским, вам нужно всего лишь изменить «Hello» на «Ola» в одном месте.
Питер Б

9
@PieterB на самом деле, "Hola"
ред.

29
@PieterB - И если вы обнаружите, что написали "Hola" с ошибкой, вам нужно только исправить это в одном месте.
Даниэль Р Хикс

Ответы:


60

Пожалуйста, простите мою память, если у меня есть это неправильно ... Javascript не мой предпочтительный язык реализации.

Есть несколько причин, по которым можно хотеть, чтобы функция без аргументов заключалась в вызове другой функции. В то время как простой вызов window.alert("Hello");- это то, что вы можете себе представить, просто вместо того, чтобы звонить напрямую sayHello().

Но что, если это еще не все? У вас есть дюжина мест, куда вы хотите позвонить sayHello()и написали window.alert("Hello");вместо этого. Теперь вы хотите это сделать window.alert("Hello, it is now " + new Date()). Если вы завершили все эти звонки, как sayHello()вы меняете его в одном месте. Если вы этого не сделали, вы измените его в дюжине мест. Это касается не повторять себя . Вы делаете это, потому что вы не хотите делать это дюжину раз в будущем.

В прошлом я работал с библиотекой i18n / l10n, которая использовала функции для локализации текста на стороне клиента. Рассмотрим sayHello()функцию. Вы можете распечатать его, holaкогда пользователь переведен на испанский язык. Это может выглядеть примерно так:

function sayHello() {
  var language = window.navigator.userLanguage || window.navigator.language;
  if(language === 'es') { window.alert('Hola'); }
   else { window.alert("Hello"); }
}

Хотя библиотека работала не так. Вместо этого он имел набор файлов, которые выглядели так:

# English file
greeting = hello
# Spanish file
greeting = hola

И тогда библиотека обнаружит настройку языка браузера и затем создаст динамические функции с соответствующей локализацией в качестве возвращаемого значения для вызова функции без аргументов на основе соответствующего файла локализации.

Мне не хватает Javascript-кодера, чтобы сказать, хорошо это или плохо ... просто так было и можно рассматривать как возможный подход.

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

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


3
window.alertтакже часто используется в качестве заполнителя во время разработки до тех пор, пока не будет спроектирован / реализован хороший модальный / всплывающий текст, так что, как и в случае с языком, его можно переключить гораздо проще Или, если у вас уже есть, обновление дизайна через несколько лет может потребовать аналогичных изменений.
Изката

34

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

 function sayHello() {
  window.alert("Hello");
 }

И это дает вам возможность изменить его позже

 function sayHello() {
  console.log("Hello");
 }

19

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

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

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

  • выполнение контракта: клиентский код может ожидать модуль с функцией sayHello. В этом случае, даже если это тривиально, функция должна быть там.

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


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

3
Другие причины хороши, но +1 за упоминание уровней абстракции. Использование четких абстракций и хороших имен функций может значительно облегчить чтение и поддержку старого кода. В тот момент, когда вызывается метод sayHello (), вы не обязательно заботитесь о том, как он реализован, вы просто хотите понять, какова цель этой строки кода. И имя функции вроде sayHello () делает это очевидным.
kkrambo

Также +1 за упоминание уровней абстракции: может случиться, что реализация функции на одном уровне абстракции состоит из одного вызова другой функции более низкого уровня абстракции. Ну и что?
Джорджио

7

Удивительно, что ни один другой человек не упомянул о тестировании.

Конкретная «завернутая» строка, которую вы выбрали, на window.alert('hello')самом деле является прекрасным примером этого. Все, что связано с windowобъектом, очень, очень больно проверять. Умножьте это на 1000 раз в своем приложении, и я гарантирую, что разработчики в конечном итоге откажутся от тестирования. С другой стороны, довольно просто забить sayHelloфункцию шпионом и проверить, что она была вызвана.

Более практичный пример - потому что действительно, кто на самом деле использует window.alert(...)в производственном коде? - проверяет системные часы. Так, например, это будет перенос DateTime.Nowв .NET, time(...)в C / C ++ или System.currentTimeMillis()в Java. Вы действительно хотите заключить тех в зависимость, которую вы можете внедрить, потому что их не только (почти) невозможно высмеять / подделать, они доступны только для чтения и не являются детерминированными . Любой тест, охватывающий функцию или метод, который непосредственно использует функцию системных часов, с большой вероятностью будет подвержен периодическим и / или случайным сбоям.

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


2

Как сказал Доваль, этот пример, вероятно, просто пытается познакомить вас с функциями. Я вообще, хотя, это полезно. В частности, указав некоторые, но не все аргументы, и передав другие, вы можете сделать более специфическую для конкретного случая функцию из более общей функции. В качестве несколько тривиального примера рассмотрим функцию сортировки, которая принимает массив для сортировки и функцию сравнения для сортировки. Указав функцию сравнения, которая сравнивает по числовому значению, я могу создать функцию sortByNumericValue, и вызовы этой функции будут намного более четкими и краткими.


2

Мышление, стоящее за вашим вопросом, кажется: «Почему бы просто не написать alert("Hello"); прямо? Это довольно просто».

Отчасти ответ заключается в том, что вы действительно не хотите звонить alert("Hello")- вы просто хотите поздороваться.

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

Это то, что понимается под такими терминами, как абстракция, косвенное обращение, «скрытие деталей реализации» и даже «выразительный код».

То же самое относится и к использованию констант вместо записи необработанных значений везде. Вы можете просто написать 3.141592...каждый раз, когда вам нужно π, но, к счастью, естьMath.PI .

Мы могли бы также посмотреть на alert()себя. Кого волнует, как он создает и отображает это диалоговое окно с предупреждением? Вы просто хотите предупредить пользователя. Между вами пишуalert("Hello") и изменением пикселей на экране, существует глубокий, глубокий стек кода и аппаратного обеспечения, при котором каждый слой сообщает следующему, чего он хочет, и этот следующий уровень заботится о деталях, пока максимально глубокий слой не перевернет несколько битов в видеопамять

Вы действительно не хотите делать все это сами, чтобы просто поздороваться.

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

Это алгебра.


-1

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

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

«Функция», которая выполняет действие, на самом деле является процедурой , которая оказывает влияние .

Любые эффекты, возникающие при вычислении значения, называются побочными эффектами , и лучше избегать их, где это возможно («Мне просто нужна была эта строка, я не знал, что это приведет к разрушению базы данных!»).

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

По той же причине мы должны избегать обработки результатов внутри процедуры. Лучше вернуть результат (если есть) нашего нашего действия и выполнить любую последующую обработку с чистыми функциями.

Если мы будем следовать этим правилам, мы можем получить такую ​​процедуру sayHello, которая не требует каких-либо данных и не дает результата. Следовательно, лучший интерфейс для этого - не иметь аргументов и не возвращать значение. Это предпочтительнее, например, для вызова «console.log» во время некоторых вычислений.

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

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


Вы, кажется, выступаете против ООП здесь, где основной принцип - «говори, не спрашивай». Следуя совету, вот что обычно приводит к коду, изобилующему бесполезными вызовами getFoo, setFoo и execute. ООП - это объединение данных и поведения, а не их разделение.
Аарона

@Aaronaught Это правда, что я против ООП, но я определенно не рекомендовал бы setFooзвонки, так как это подразумевает изменяемые данные. Изменение содержимого переменных / свойств является эффектом, который приводит к тому, что все вычисления, использующие эти данные, становятся нечистыми. Я бы не рекомендовал executeвызовы как таковые, но я бы порекомендовал runFooпроцедуры для выполнения действий на основе их аргументов.
Warbo

-2

Я бы сказал, что это сочетание ответов, данных @MichaelT и @Sleiman Jneidi.

Возможно, в коде есть несколько мест, где вы хотите поприветствовать пользователя сообщением Hello, чтобы вы упаковали эту идею в метод. В методе вы можете перевести его или расширить простое «Привет», отображая более длинный текст. Также вы можете захотеть использовать приятный диалог JQuery вместо предупреждения.

Дело в том, что реализация находится в одном месте. Вам просто нужно изменить код в одном месте. (SayHello (), возможно, слишком простой пример)


@downvoter (s) - делитесь своими знаниями со всеми остальными. Мой пост просто неправильный, плохо написан или как?
Пол
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.