2015-06-26 4 views
1

У меня следующая ситуация:Идентичный шаблон наследования в нескольких классах

class A < CommonParent 
    ... some code ... 

    class IdenticalDescendent < self 
    identical_statement_0 
    identical_statement_1 
    end 
end 

class B < CommonParent 
    ... some other code ... 

    class IdenticalDescendent < self 
    identical_statement_0 
    identical_statement_1 
    end 
end 

меня такая ситуация много. Например, в моем приложении около сорока классов IdenticalDescendent. Мне нравится шаблон, он позволяет мне позвонить A::IdenticalDescendent или B::IdenticalDescendent или что угодно, чтобы получить доступ к определенным связанным с ними поведениям в разных доменах (задано A или B). По причинам я не могу просто полностью отвлечь проблему, перестроив кластеры поведения.

Таким образом, общая форма моего вопроса заключается в том, как автоматизировать генерацию IdenticalDescendent во всех этих случаях. Там есть потомки CommonParent, которые не вызывают этот шаблон, поэтому действия, вероятно, не должны происходить. Я предполагаю, что это должно произойти в Mixin или что-то, но я считаю, что если я просто пытаюсь сделать:

class A < CommonParent 
    include CommonBehaviour 

    ... some code ... 
end 

module CommonBehaviour 
    ... what ... 
end 

Я не могу понять, как писать CommonBehaviour, чтобы для IdenticalDescendent спускаться с класса, включая ,

Помогите мне StackOverflow, вы - моя единственная надежда.

+0

Приведите пример реальной жизни, пожалуйста. Трудно понять, чего вы пытаетесь достичь. До сих пор остается только догадываться: вы делаете это неправильно. – mudasobwa

+0

Я получил его покрытый, спасибо людям! – wmjbyatt

ответ

0

Я считаю, что вы можете автоматизировать свой образец, используя функцию обратного вызова (крюк) Class#inherited:

class CommonParent 
    def self.inherited(klass) 
    return unless klass.superclass == CommonParent 
    klass.const_set(:Descendent, Class.new(klass) do 
     def x 
     puts "in x" 
     end 
    end) 
    end 
end 

class A < CommonParent 
    def a 
    puts "in a" 
    end 
end 

d = A::Descendent.new #=> #<A::Descendent:0x007f99620172e8> 
d.a     # in a 
d.x     # in x 

class B < CommonParent 
    def b 
    puts "in b" 
    end 
end 

d = B::Descendent.new #=> #<B::Descendent:0x007f99618b18f0> 
d.b     # in b 
d.x     # in x 
d.a     #=> NoMethodError:... (as expected) 

Обратите внимание, что без:

return unless klass.superclass == CommonParent 

создание A::Descendent вызовет inherited с klass => Descendent, в результате чего анонимный подкласс Descendent, который будет создан, и т. д., что приведет к «слишком глубокому исключению уровня стека».

-1

Я бы предложил разделить класс потомков и метод генерации. Конечно, вы могли бы бросить все в блок class_eval (который положительно поникнет).

-то вроде следующего (полностью непроверенный)

module CommonDescendants 
    Descendant = Class.new(self) do 
    include CommonDescendantMethods 
    end 
end 

module CommonDescendantMethods 
end 

class A < CommonParent 
    extend CommonDescendants 
end 
+0

Поведение, которое вы собираетесь использовать для 'Descendant = Class.new (self)', - это то, что мне не хватает. Этот код не работает, потому что в контексте 'self' есть модуль' CommonDescendants', а не класс включения. Это было сказано, я забыл, что синтаксис 'Class.new' принял блок, поэтому я могу поиграть с этим, чтобы посмотреть, смогу ли я его работать. – wmjbyatt

+0

Я отработал это отсюда, спасибо за вашу помощь! – wmjbyatt

+0

Разбор модуля CommonDescendants вызывает исключение, '' TypeError: суперкласс должен быть классом (данный модуль) ". Это потому, что 'self' является' CommonDescendents' (я исправил орфографию), который не является классом. 'extend CommonDescendents' не имеет смысла, потому что' CommonDescendents' не содержит методов и методов класса, которые не желательны в любом случае.Я отклонил ваш ответ, который я редко делаю не только потому, что это неправильно, а потому, что вы представили его без тестирования. –

1

Ответ, который я искал, чтобы использовать блок для обозначения Class.new внутри self.included обратного вызова. У меня есть это сейчас:

module CommonDescendant 
    def self.included(base) 
    descendant_class = Class.new(base) do 
     ... put my desired common behavior here ... 
    end 

    base.const_set :Descendant, descendant_class 
    end 
end 

class A 
    include CommonDescendant 

    ... unique behavior ... 
end 

class B 
    include CommonDescendant 

    ... unique other behavior ... 
end 

И это дает нам дизайн, который я хочу!

+0

Выглядит хорошо. Вы можете поместить 'include CommonDescendent' в метод' included'. (См. Мой ответ.) Я заметил, что вы решили изменить написание «потомка». Извините, но «потомок» применим только к растениям и животным (включая людей). Подождите: что случилось с «CommonParent»? –

+0

Я не следую вашему коду здесь, частично потому, что нет ссылки на «CommonParent». В любом случае «включен» будет срабатывать каждый раз при создании подкласса «CommonParent» или подкласса этого подкласса и т. Д., Что приведет к «слишком глубокому уровню стека». (См. Мой ответ, как этого избежать.) Во-вторых, 'include CommonDescendant' ничего не делает, потому что этот модуль не содержит методов экземпляра. –