Когда я должен использовать метод псевдонимов? - Рубин - PullRequest
0 голосов
/ 09 июня 2019

Я просмотрел и не видел ответа:

Что бы вы использовали метод псевдонима?

class Vampire
 attr_reader :name, :thirsty

 alias_method :thirsty?, :thirsty
end

Единственная причина, по которой я бы использовал одну из них, - это возможность использовать вопросительный знак с любым определенным мною методом? Я считаю, что вы не можете использовать вопросительные знаки с переменными экземпляра.

Ответы [ 2 ]

3 голосов
/ 09 июня 2019

Есть две причины, по которым одна будет использовать Module#alias_method, одна является текущей и действительной, а другая устарела и в действительности никогда не была необходима.

Первая причина заключается в том, чтоВы просто хотите иметь два метода с разными именами, которые делают одно и то же.Одной из причин этого может быть то, что есть два одинаково широко используемых термина для одного и того же действия, и вы хотите, чтобы людям было проще писать код, понятный их сообществу.(Некоторыми примерами в базовой библиотеке Ruby являются методы коллекций, имена которых знакомы людям, приходящим из функциональных языков программирования, таким как map, reduce, людям из семейства языков программирования Smalltalk, таким как collect, inject, select и стандартные английские имена, такие как find_all.) Другая возможность состоит в том, что вы создаете Fluent Interface и хотите, чтобы он читался более свободно, например так:

play this
and that
and something_else

В этом случае and может быть псевдонимом для play.

Другая, связанная с этим причина (назовем ее причиной 1.5), заключается в том, что вы хотите реализовать некоторый протокол иу вас уже есть метод, который правильно реализует семантику этого протокола, но у него неправильное имя.Давайте предположим гипотетическую структуру коллекций, которая имеет два метода map_seq и map_nonseq.Первый выполняет map и гарантирует порядок побочных эффектов, тогда как второй не гарантирует порядок побочных эффектов и может даже выполнять операцию отображения асинхронно, одновременно или параллельно.Эти два метода на самом деле имеют разную семантику, но если ваша структура данных не поддается параллелизации, вы можете просто реализовать map_seq и сделать map_nonseq псевдонимом.

В этом случае драйвер не являетсянастолько, что вы хотите предоставить два имени для одной и той же операции, но вы хотите предоставить одну и ту же реализацию для двух имен (если это предложение имеет смысл :-D).

Вторая основная причина, почему alias_method использовался в прошлом, имеет отношение к важной детали его семантики: когда вы переопределяете или monkey-patch один из двух методов, это повлияет только на это имя, но не на другое.В прошлом это использовалось для обтекания поведения метода, например:

class SomeClass
  def some_method
    "Hello World"
  end
end

Это немного скучно.Мы хотим, чтобы наш метод ШУТ!Но мы не хотим просто копировать и повторно реализовывать метод, мы хотим повторно использовать его внутреннюю реализацию без необходимости знать, как он реализован внутри.И мы хотим сделать это, чтобы все клиенты этого метода имели кричащее поведение.Популярный способ сделать это был примерно такой:

class SomeClass
  alias_method :__some_method_without_shouting :some_method

  def __some_method_with_shouting
    __some_method_without_shouting.upcase
  end

  alias_method :some_method :__some_method_with_shouting
end

В этом примере мы используем alias_method, чтобы создать «резервную копию» метода, который мы используем для обезьяньего исправления, чтобы мы могли вызвать егоизнутри нашей залатанной обезьяной версии метода.(В противном случае этот метод исчезнет.) На самом деле это вариант использования, приведенный в документации alias_method.

. Эта идиома была настолько популярна и широко использовалась, что некоторые библиотеки даже предоставили ее реализацию,например, ActiveSupport Module#alias_method_chain.

Обратите внимание, однако, что эта идиома имеет некоторые не очень хорошие свойства.Во-первых, мы загрязняем пространство имен всеми этими _with_ и _without_ методами.Например, когда вы посмотрите на методы объекта, вы увидите все это.Другая проблема заключается в том, что люди все еще могут вызывать старый метод напрямую, но, вероятно, у нас была причина для его исправления, потому что мы не хотим старого поведения.(В противном случае мы могли бы просто создать метод с новым именем, которое вызывает старое, например, shout.)

Всегда была лучшая альтернатива, которая не была так широко использована:

class SomeClass
  some_method_without_shouting = instance_method(:some_method)

  define_method(:some_method) do
    some_method_without_shouting.bind(self).().upcase
  end
end

Здесь мы храним старый метод в локальной переменной и используем блок для определения нового метода (через Module#define_method).Локальная переменная выходит из области видимости в конце тела класса, поэтому к ней больше никогда не будет никакого доступа.Но блоки - это замыкания , они закрываются в окружающей их лексической среде, и, таким образом, блок, переданный в define_methodтолько этот блок), все еще имеет доступ к привязке переменной.Таким образом, старая реализация полностью скрыта и загрязнение пространства имен отсутствует.

Однако в Ruby 2.0 существует гораздо лучшее решение для обертывания этого метода: Module#prepend.Прелесть prepend в том, что это «просто наследование», и мы можем просто использовать super:

module Shouter
  def some_method
    super.upcase
  end
end

class SomeClass
  prepend Shouter
end

Module#prepend - причина, по которой Module#alias_method_chain устарела в ActiveSupport 5.0 и удаленав 5.1, например.Все эти искажения просто больше не нужны.

Итак, подведем итог: было две основные причины использования alias_method: буквальное создание псевдонима, то есть двух имен для одной и той же операции, и создание резервной копии.для метода упаковки.Второй больше не действителен и, возможно, возможно никогда не был.Сегодня только первая причина является допустимой причиной использования alias_method.

3 голосов
/ 09 июня 2019

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

В вашем фрагменте кода у вас есть фрагмент кода, который читает attr_reader :thirsty, который в основном является геттером для переменной экземпляра с тем же именем (@thirsty)

def thirsty
  @thirsty
end

В исходном фрагменте кода у вас было утверждение:

refute vampire.thirsty?

У вас также был код, который просто возвращал true для thirsty? метода, который не подтвердил ваше утверждение.

Есть как минимум два способа изменить свой код, чтобы сработал вызов thirsty? и ваше утверждение прошло:

Создайте метод, который вызывает читатель thirsty, или получите доступ к самой переменной экземпляра @thirsty:

def thirsty?
  thirsty # or @thirsty
end

Другой способ - использовать alias_method, что функционально эквивалентно приведенному выше. Это псевдонимы от thirsty? до thirsty, то есть attr_reader, который читает из @thirsty переменной экземпляра

Ссылка на другой ответ, который я дал

Возможно, вам лучше вообще не использовать attr_reader, а просто делать так, как Серхио отметил в своем комментарии:

class Vampire
  def initialize(name)
    @name = name
    @thirsty = true
  end

  def thirsty?
    @thirsty
  end

  def drink
    @thirsty = false
  end
end
...