Одинаковая модель наследования в нескольких классах

У меня следующая ситуация:

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

class A < CommonParent
  include CommonBehaviour

  ... some code ...
end

module CommonBehaviour
  ... what ...
end

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

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

3 ответа

Решение

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

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 быть созданным и т. д., что приводит к "слишком глубокому исключению на уровне стека".

Ответ, который я искал, должен использовать блочную нотацию для 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

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

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

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

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

module CommonDescendantMethods
end

class A < CommonParent
  extend CommonDescendants
end
Другие вопросы по тегам