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

Неужели "обезьяна патчет" действительно так плохо?

Некоторые языки, такие как Ruby и JavaScript, имеют открытые классы, которые позволяют изменять интерфейсы даже основных классов, таких как числа, строки, массивы и т.д. Очевидно, что это может запутать других, знакомых с API, но есть веские основания для избегайте этого в противном случае, предполагая, что вы добавляете к интерфейсу и не изменяете существующее поведение?

Например, было бы неплохо добавить реализацию Array.map для веб-браузеров, которые не реализуют выпуск ECMAScript 5th (и если вы не нужен весь jQuery). Или ваши массивы Ruby могут извлечь выгоду из метода "суммы", который использует "инъекцию". До тех пор, пока изменения будут изолированы от ваших систем (например, не являются частью программного пакета, который вы выпускаете для распространения), есть ли веская причина не использовать эту языковую функцию?

4b9b3361

Ответ 1

В Википедии есть краткий обзор подводных камней обезглавливания обезьян:

http://en.wikipedia.org/wiki/Monkey_patch#Pitfalls

Там время и место для всего, также для патчей обезьян. У опытных разработчиков есть много методов, чтобы их рукава и узнать, когда их использовать. Редко бывает, что это "зло", просто бессмысленное использование этого.

Ответ 2

Обезвреживание обезьян, как и многие инструменты в программном инструменте, можно использовать как для добра, так и для зла. Вопрос в том, где, в целом, такие инструменты, как правило, наиболее широко используются. По моему опыту с Рубином весы сильно зависят от "злой" стороны.

Итак, какое "злое" использование обезьян-патчей? Ну, обезьяна-патч в целом оставляет вас широко открытыми для крупных, потенциально неразрушаемых столкновений. У меня есть класс A. У меня есть какой-то модуль исправления обезьян MB, который исправляет A, чтобы включить method1, method2 и method3. У меня есть еще один модуль исправления обезьяны MC, который также исправляет A, чтобы включить method2, method3 и method4. Теперь у меня есть привязка. Я вызываю instance_of_A.method2: чей метод вызывается? Ответ на это может зависеть от множества факторов:

  • В каком порядке я ввел модули исправления?
  • Являются ли исправления применены сразу или в каком-то условном случае?
  • AAAAAAARGH! СПАЙДЕРЫ ПУТЮТЬ ЕГО ГЛАЗЫ ИЗ ВНУТРИ!

ОК, поэтому # 3, возможно, чуть более-мелодраматический....

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

Наличие патчей для обезьян - неплохо, если вы являетесь ответственным разработчиком. К сожалению, IME, что имеет тенденцию к происходящему, состоит в том, что кто-то видит исправление обезьяны и говорит: "Сладкий, я просто обезумел бы это, вместо того, чтобы проверять, не могут ли другие механизмы быть более подходящими". Это ситуация, примерно аналогичная Lisp базам кода, созданным людьми, которые достигают макросов, прежде чем они думают, что просто делают это как функцию.

Ответ 3

Пока изменения изолированы ваши системы (например, не являющиеся частью программного пакета, который вы выпускаете для распределение) есть веская причина не использовать этот язык особенность?

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

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

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

Чтобы проиллюстрировать конкретную ошибку в JavaScript.

Array.protoype.map = function map() { ... };

var a = [2];
for (var k in a) {
    console.log(a[k]);
} 
// 2, function map() { ... }

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

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

Ответ 4

Совершенно разумно использовать "исправление обезьян", чтобы исправить определенную, известную проблему, где альтернативой было бы ждать исправления, чтобы исправить ее. Это означает, что вы берете на себя ответственность за исправление чего-либо, пока не появится "правильное", официально выпущенное исправление, которое вы можете развернуть.

Рассматриваемое мнение Гилада Брахи о патче обезьян: http://gbracha.blogspot.com/2008/03/monkey-patching.html

Ответ 5

Условия вашего описания - добавление (не изменяющееся) существующего поведения, а не освобождение кода во внешний мир - кажутся относительно безопасными. Однако проблемы могут возникнуть, если следующая версия Ruby или JavaScript или Rails изменяет их API. Например, что, если какая-то будущая версия jQuery проверяет, уже ли определена Array.map и предполагает ли она версию карты EMCA5Script, когда на самом деле это ваш обезьян-патч?

Аналогично, что, если вы определяете "сумму" в Ruby, и однажды вы решите, что хотите использовать этот рубиновый код в Rails или добавить в свой проект Gemo Active Support. Active Support также определяет метод sum (on Enumerable), поэтому существует конфликт.

Ответ 6

Что касается Javascript:

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

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

Это именно то, что произошло с Array.prototype.flatten и Array.prototype.contains. Короче говоря, для этих методов была составлена спецификация, их предложения дошли до стадии 3, а затем браузеры начали ее отправку. Но в обоих случаях было обнаружено, что существуют древние библиотеки, которые исправляли встроенный объект Array своими собственными методами с тем же именем, что и новые методы, и имели другое поведение; в результате веб-сайты сломались, браузерам пришлось отказаться от реализации новых методов, а спецификацию пришлось редактировать. (Методы были переименованы.)

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

Все, что сказано, относительно конкретного примера в вашем вопросе:

Например, было бы неплохо добавить реализацию Array.map в веб-браузеры, которые не реализуют ECMAScript 5-е издание

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

Полифилл - это код, который реализует функцию в веб-браузерах, которые не поддерживают эту функцию. Чаще всего это относится к библиотеке JavaScript, которая реализует веб-стандарт HTML5, либо установленный стандарт (поддерживаемый некоторыми браузерами) в старых браузерах, либо предлагаемый стандарт (не поддерживаемый никакими браузерами) в существующих браузерах.

Например, если вы написали polyfill для Array.prototype.map (или, если взять более новый пример, для Array.prototype.flatMap), который полностью соответствовал официальной спецификации Stage 4, а затем запустили код, определяющий Array.prototype.flatMap для браузеров, у которых его еще нет:

if (!Array.prototype.flatMap) {
  Array.prototype.flatMap = function(...
    // ...
  }
}

Если ваша реализация верна, это прекрасно, и это очень часто делается во всем Интернете, так что устаревшие браузеры могут понимать новые методы. Polyfill.io является распространенным сервисом для такого рода вещей.