2011-02-04 2 views

ответ

25

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% всех языков OO, поддерживает только первый. (В некотором смысле вложенные классы наследуют не только функции своего суперкласса, но и особенности окружающего их класса.)

+0

Спасибо за добавление разъясняющего комментария, +1 от меня. –

+2

@Michael Kohl: Это не вложенные классы, это константы с именами. Первое предложение правильное. Я предположил, что это произошло, так это то, что второе предложение первоначально прочитало что-то вроде «вложенных констант, относящихся к классам», и они подумали, что это звучит немного вынужденно, поэтому они сократили его до «вложенных классов», не осознавая свою ошибку. Как все знают, что Ruby не имеет методов класса, но мы по-прежнему называем их «методами класса», потому что правильная формулировка «методы экземпляра класса Singleton класса класса» настолько неудобна. К сожалению, этот –

+0

... вид жаргонного ярлыка может привести к значительной путанице для посторонних и новичков. Просто взгляните на все вопросы здесь, на StackOverflow, которые спрашивают: «Я знаю, как делать X (издевку, переопределение, переписывание, сглаживание, обертывание, обрезание) с помощью обычных методов, но как это сделать с помощью методов класса?» Из-за ярлыка-жаргона, который мы используем, они не понимают, что на самом деле они уже знают ответ на свой вопрос, потому что * нет такой вещи, как метод класса *. –

0

Был ли 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.

+0

Нет, я действительно имел в виду внутренние классы, стиль «Новостной». – nes1983

10

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

class A 
    def a 
    puts "hello from a" 
    end 

    class B 
    def b 
     Module.nesting[1].new.a() 
    end 
    end 
end 
+0

Это может работать только в том случае, если все это находится внутри модуля. –

3

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

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" 
4

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

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 
1

Ну, в зависимости от ваших обстоятельств на самом деле есть решение, довольно легкое в этом. 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, который обрабатывает только определенные действия, но может легко вызвать базовые методы контроллера (однако вы хотите отправить родительский контроллер в качестве аргумента в инициализаторе).

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

 Смежные вопросы

  • Нет связанных вопросов^_^