Как заставить alias_method в Ruby использовать пользовательский метод подкласса? - PullRequest
3 голосов
/ 23 июня 2019

Допустим, у меня есть базовый класс с тремя подклассами.Базовый класс имеет метод, общий для большинства подклассов, и имеет псевдоним:

class Beer
  def bottle_content
    '250 ml'
  end
  alias_method :to_s, :bottle_content
end

class Heineken < Beer
end

class Stella < Beer
end

class Duvel < Beer
  def bottle_content
    '330 ml'
  end
end

Теперь, если метод to_s вызывается для расходящегося экземпляра подкласса Duvel,250 ml возвращается вместо 330 ml.

Я понимаю, почему;псевдоним сделан на уровне суперкласса.И я знаю, что это можно исправить, переопределив alias_method в расходящемся классе.Но есть ли другой способ сделать это?

Очевидно, что использование метода для to_s будет работать:

class Beer
  def bottle_content
    '250 ml'
  end
  def to_s; bottle_content; end
end

Но, может быть, есть более элегантный подход?

Ответы [ 3 ]

2 голосов
/ 24 июня 2019

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

require 'forwardable'

class Beer
  extend Forwardable

  def bottle_content
    '250 ml'
  end

  def_delegator :self, :bottle_content, :to_s
end

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

irb(main):001:0> puts Duvel.new
330 ml
=> nil
2 голосов
/ 23 июня 2019

Это потому, что alias_method обрабатывает self во время выполнения.

Редактировать Как прокомментировал @ Jörg, мое решение может вызвать некоторые проблемы в зависимости от вашего приложения (многопоточная среда…). Реализация to_s, как вы предложили, вероятно, лучшая, и не менее элегантная IMO.

Одним из способов было бы объявить псевдоним при инициализации (в классе).

class Beer
  def initialize
    self.class.alias_method :to_s, :bottle_content
  end

  def bottle_content
    '250 ml'
  end
end

Таким образом, псевдоним создается в классе экземпляра, т.е. Duvel в вашем примере.

0 голосов
/ 24 июня 2019

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

Использование обратного вызова Класс # унаследован , однако мы можем сделать что-то близкое к тому, что вы хотите достичь:

class Beer
  def bottle_content
    '250 ml'
  end

  alias_method :to_s, :bottle_content

  def self.inherited(klass)
    klass.define_method(:to_s) { bottle_content }
  end
end

class Duvel < Beer
  def bottle_content
    '330 ml'
  end
end

Beer.new.to_s
  #=> "250 ml" 
Duvel.new.to_s
  #=> "330 ml"

Проблема в том, чтоBeer::inherited вызывается сразу после выполнения class Duvel < Beer, до определения метода Duvel#bottle_content.Если бы мы изменили рабочую строку с Beer::inherited на

klass.alias_method(:to_s, :bottle_content)

, это связало бы Duvel#to_s с Beer#bottle_content, так как Duvel#bottle_content еще не было определено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...