Как вложенный класс может получить доступ к методу во внешнем классе в Ruby? - PullRequest
20 голосов
/ 05 февраля 2011
def class A
  def a
    raise "hi" #can't be reached
  end

  class B
    def b
      a() #doesn't find method a.
    end
  end
end

Я хочу вызвать a из b и вызвать исключение.Как я могу?

Ответы [ 6 ]

27 голосов
/ 05 февраля 2011

В Ruby нет вложенных классов.

Единственный способ наследовать поведение - это наследование.

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

  • BETA , язык, который ввел вложенные классыклассы (и их преемник gbeta )
  • Newspeak

Я не знаю других.

В Java есть конструкция, называемая вложенными классами, но у них есть некоторые неудачные конструктивные ограничения.

В приведенном выше примере это не класс B, который вложен в A, это константа B, которая вложена в A.Подумайте об этом:

C = A::B

Теперь класс доступен под двумя именами: A::B и C.Должно быть сразу очевидно, что C является глобальным и не вложен в A.(Ну, на самом деле, C вложено в Object, потому что на самом деле глобальных констант тоже нет, но это не относится к делу.) Но поскольку C и A::B - это один и тот же класс, очевидно, что это невозможнокак вложенные, так и не вложенные.Единственный логический вывод заключается в том, что сам класс не является вложенным.

Отличительной особенностью вложенных классов является то, что поиск метода идет по двум измерениям: вверх по цепочке наследования и наружу черезвложенности.Ruby, как и 99,9% всех ОО-языков, поддерживает только первый.(В некотором смысле вложенные классы наследуют не только свойства своего суперкласса, но также и свойства окружающего их класса.)

11 голосов
/ 05 февраля 2011

Это только для лулза:

class A
  def a
    puts "hello from a"
  end

  class B
    def b
      Module.nesting[1].new.a()
    end
  end
end
4 голосов
/ 19 февраля 2012

Я обычно делаю что-то вроде этого:

class A
  def a
    puts "hi"
  end

  def createB
    B.new self
  end

  class B
    def initialize(parent)
      @parent=parent
    end

    def b
      @parent.a
    end
  end
end

A.new.createB.b
3 голосов
/ 25 мая 2011

Если вы хотите, чтобы вложенный класс расширял внешний класс, то сделайте так:

class Outer

  class Inner < Outer
    def use_outer_method
      outer_method("hi mom!")
    end
  end

  def outer_method(foo)
    puts foo
  end

end

foo = Outer::Inner.new
foo.use_outer_method        #<= "hi mom"
foo.outer_method("hi dad!") #<= "hi dad"
1 голос
/ 13 февраля 2015

Ну, в зависимости от ваших обстоятельств, есть решение, довольно простое.Ruby позволяет перехватывать вызовы методов, которые не перехвачены объектом.Итак, для вашего примера вы можете сделать:

def class A
  def a
    raise "hi" #can't be reached
  end

  class B
    def initialize()
      @parent = A.new
    end

    def b
      a() #does find method a.
    end

    def method_missing(*args)
      if @parent.respond_to?(method)
        @parent.send(*args)
      else
        super
      end
    end
  end
end

Итак, если вы сделаете это:

A::B.new().b

вы получите:

!! #<RuntimeError: hi>

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

0 голосов
/ 05 февраля 2011

Был ли a метод класса для класса A?

class A
  def self.a
    raise "hi"
  end
  class B
    def b
      A::a 
    end
  end
end

A::B.new.b

Если вы хотите сохранить его как метод экземпляра, вам, очевидно, придется обращаться к нему в экземпляре, как, например, A.new.a.

...