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

Есть ли способ создать методы только для экземпляра класса Ruby изнутри этого экземпляра?

Пусть существует class Example, определяемый как:

class Example
  def initialize(test='hey')
    self.class.send(:define_method, :say_hello, lambda { test })
  end
end

При вызове Example.new; Example.new получаем a warning: method redefined; discarding old say_hello. Это, я заключаю, должно быть потому, что он определяет метод в реальном классе (что имеет смысл, из синтаксиса). И это, конечно, окажется катастрофическим, если в их методах есть несколько экземпляров Example с разными значениями.

Есть ли способ создать методы только для экземпляра класса из этого экземпляра?

4b9b3361

Ответ 1

Вам нужно получить ссылку на экземпляр singleton class, класс, который содержит все экземпляры конкретных объектов, и определить метод на нем. В рубине 1.8, это выглядит немного грязно. (если вы найдете более чистое решение, дайте мне знать!)

Ruby 1.8

class Example
  def initialize(test='hey')
    singleton = class << self; self end
    singleton.send :define_method, :say_hello, lambda { test }
  end
end

Ruby 1.9, однако, обеспечивает гораздо более легкий способ.

Ruby 1.9

class Example
  def initialize(test='hey')
    define_singleton_method :say_hello, lambda { test }
  end
end

Ответ 2

Прежде всего, небольшой совет:

self.class.send(:define_method, :say_hello, lambda { test })

Вы можете сделать этот взгляд немного приятнее, используя новый литерал proc в Ruby 1.9:

self.class.send(:define_method, :say_hello, -> { test })

Но вам это не нужно. Ruby имеет что-то, называемое блоками, которые в основном представляют собой фрагмент кода, который вы можете передать в качестве аргумента для метода. Фактически, вы уже использовали блоки, так как lambda - это просто метод, который принимает блок как аргумент и возвращает Proc. Тем не менее, define_method уже принимает блок в любом случае, нет необходимости передавать блок в lambda, который преобразует его в Proc, который он передает на define_method, который затем преобразует его обратно в блок:

self.class.send(:define_method, :say_hello) { test }

Как вы уже заметили, вы определяете метод в неправильном классе. Вы определяете его в классе Example, так как внутри метода экземпляра, такого как initialize, self - текущий объект (т.е. ex1 или ex2 в примере @mikej), что означает, что self.class ex1, который равен Example. Таким образом, вы переписываете один и тот же метод снова и снова.

Это приводит к следующему нежелательному поведению:

ex1 = Example.new('ex1')
ex2 = Example.new('ex2') # warning: method redefined; discarding old say_hello
ex1.say_hello            # => ex2 # Huh?!?

Вместо этого, если вы хотите использовать одноэлементный метод, вам нужно определить его в классе singleton:

(class << self; self end).send(:define_method, :say_hello) { test }

Это работает по назначению:

ex1 = Example.new('ex1')
ex2 = Example.new('ex2')
ex1.say_hello            # => ex1
ex2.say_hello            # => ex2

В Ruby 1.9 существует метод, который делает это:

define_singleton_method(:say_hello) { test }

Теперь это работает так, как вы этого хотите, но здесь проблема более высокого уровня: это не Ruby-код. Это синтаксис Ruby, но это не Ruby-код, это Scheme.

Теперь, схема - блестящий язык, и писать код схемы в синтаксисе Ruby, конечно, не так уж плохо. Это чертовски чертит из написания Java или PHP-кода в синтаксисе Ruby, или, как в случае с вчерашним вопросом StackOverflow, код Fortran-57 в синтаксисе Ruby. Но это не так хорошо, как писать Ruby-код в синтаксисе Ruby.

Схема - функциональный язык. Функциональные языки используют функции (точнее, замыкания функций) для инкапсуляции и состояния. Но Ruby не является функциональным языком, это объектно-ориентированный язык, а языки OO используют объекты для инкапсуляции и состояния.

Итак, замыкания функций становятся объектами, а захваченные переменные становятся переменными экземпляра.

Мы также можем прийти к этому с совершенно другого ракурса: то, что вы делаете, заключается в том, что вы определяете метод singleton, который является методом, целью которого является определение поведения, характерного для одного объекта. Но вы определяете этот метод singleton для каждого экземпляра класса, и вы определяете один и тот же метод singleton для каждого экземпляра класса. У нас уже есть механизм определения поведения для каждого экземпляра класса: методы экземпляра.

Оба этих аргумента исходят из совершенно противоположных направлений, но они приходят в один и тот же пункт назначения:

class Example
  def initialize(test='hey')
    @test = test
  end

  def say_hello
    @test
  end
end

Ответ 3

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

    string = "String"
    string.instance_eval do
      def new_method
        self.reverse
      end
    end