Могу ли я сделать методы родительского класса частными, но разрешить их вызов их дочерним классам только через наследование? - PullRequest
0 голосов
/ 30 октября 2019

Допустим, у меня есть трехуровневая иерархия классов наследования:

Building
  ⬑ OfficeTower
  ⬑ House
    ⬑ RowHouse
    ⬑ DetachedHouse

Я хочу, чтобы классы OfficeTower и House наследовали метод класса от класса Building, который будет перечислятьвсе детские классы. Я хочу определить этот метод только один раз, в классе Building, но я на самом деле не хочу, чтобы метод был общедоступным при вызове напрямую из класса Building.

Вот что я попробовал:

class Building

  private

  def self.subclasses
    # list all subclasses
    ObjectSpace.each_object(Class).select { |c| c < self }
  end

end

class OfficeTower < Building
end

class House < Building
end

class RowHouse < House
end

class DetachedHouse < House
end

Метод класса subclasses работает, как и ожидалось, при вызове дочернего класса Building:

House.subclasses
=> [DetachedHouse, RowHouse] # This is good.

Я ожидал, что метод subclasses вызовет NoMethodError при вызове непосредственно из класса Building. Но этого не произошло:

Building.subclasses  
=> [DetachedHouse, OfficeTower, RowHouse, House] # I expected NoMethodError!

Почему в этом случае private не работает? Есть ли лучший способ сделать этот метод недоступным при вызове непосредственно из класса Building? Должен ли я использовать композицию вместо наследования (т.е. создать модуль Buildable с методом класса subclasses и включить этот модуль в OfficeTower и House)? Это кажется мне более грязным ...

1 Ответ

3 голосов
/ 30 октября 2019

Почему приватная работа в этом случае не работает?

Поскольку private влияет только на методы экземпляра, а не на методы класса. Я обычно использую private_class_method для этого

  private_class_method def self.subclasses
    # list all subclasses
    ObjectSpace.each_object(Class).select { |c| c < self }
  end

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

class << self
  private

  def subclasses
    # list all subclasses
    ObjectSpace.each_object(Class).select { |c| c < self }
  end
end

Обратите внимание, что конфиденциальностьнаследуется, и вы не сможете вызвать метод на House. Я бы оставил метод открытым и поднял бы его, если self равно Building

  def self.subclasses
    raise 'use a subclass' if self == Building

    # list all subclasses
    ObjectSpace.each_object(Class).select { |c| c < self }
  end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...