Ruby - Наследование структур и названных параметров

Этот вопрос строго о стиле поведения, поэтому, пожалуйста, "почему в широком мире спорта вы так делаете?"

Этот код INCORRECT, но он должен показать, что я пытаюсь понять о Ruby Structs:

class Person < Struct.new(:name, :last_name)
end

class ReligiousPerson < Person(:religion)
end

class PoliticalPerson < Person(:political_affiliation)
end

### Main ###

person = Person.new('jackie', 'jack')
pious_person = ReligiousPerson.new('billy', 'bill', 'Zoroastrianism')
political_person = PoliticalPerson.new('frankie', 'frank', 'Connecticut for Lieberman')

Как вы можете видеть, есть попытка определить наследование класса с помощью Structs. Тем не менее, Ruby становится неуклюжим, когда вы пытаетесь инициализировать ReligiousPerson или PolicyPerson, конечно. Итак, учитывая этот иллюстративный код, как можно наследовать именованные параметры, используя этот тип наследования классов, используя Structs?

Ответы

Ответ 1

Вы можете определить новые структуры, основанные на Person:

class Person < Struct.new(:name, :last_name)
end

class ReligiousPerson < Struct.new(*Person.members, :religion)  
end

class PoliticalPerson < Struct.new(*Person.members, :political_affiliation)
end

### Main ###

person = Person.new('jackie', 'jack')
p pious_person = ReligiousPerson.new('billy', 'bill', 'Zoroastrianism')
p political_person = PoliticalPerson.new('frankie', 'frank', 'Connecticut for Lieberman')

Результат:

#<struct ReligiousPerson name="billy", last_name="bill", religion="Zoroastrianism">
#<struct PoliticalPerson name="frankie", last_name="frank", political_affiliation="Connecticut for Lieberman">

Непосредственно после отправки моего ответа у меня возникла идея:

class Person < Struct.new(:name, :last_name)
  def self.derived_struct( *args )
    Struct.new(*self.members, *args)
  end
end

class ReligiousPerson < Person.derived_struct(:religion)  
end

class PoliticalPerson < Person.derived_struct(:political_affiliation)
end

### Main ###

person = Person.new('jackie', 'jack')
p pious_person = ReligiousPerson.new('billy', 'bill', 'Zoroastrianism')
p political_person = PoliticalPerson.new('frankie', 'frank', 'Connecticut for Lieberman')

Прекрасно работает!

Вы также можете добавить #derived_struct в Struct:

class Struct
  def self.derived_struct( *args )
    Struct.new(*self.members, *args)
  end
end

Ответ 2

Ruby становится расстроенным, когда вы пытаетесь инициализировать ReligiousPerson или PolicyPerson, конечно

Я думаю, что более вероятно, что это ошибка, когда вы пытаетесь определить ReligiousPerson и PoliticalPerson. Это связано с тем, что Person(:religion) выглядит как попытка вызвать Person, как если бы это была функция. Очевидно, что это не сработает, потому что Person - это класс.

Он отлично подходит для подкласса Person:

class ReligiousPerson < Person
  attr_accessor :religion

  def initialize(name, last_name, religion)
    super(name, last_name)
    @religion = religion
  end
end

pious_person = ReligiousPerson.new('billy', 'bill', 'Zoroastrianism')
pious_person.religion #=> "Zoroastrianism"

Struct.new на самом деле не делает ничего особенного, просто динамически создавая класс на основе значений, которые вы передаете. Затем вы создаете новый класс (Person), который подклассифицирует класс, созданный с помощью Struct.new:

new_struct = Struct.new(:name, :last_name)
class Person < new_struct
end  

Person.superclass == new_struct #=> true

Кроме того, вы можете обратить внимание на это свойство некоторых предыдущих ответов:

class Person < Struct.new(:name, :last_name)
end

class ReligiousPerson < Struct.new(*Person.members, :religion)  
end

ReligiousPerson.ancestors.include?(Struct) #=> true
ReligiousPerson.ancestors.include?(Person) #=> false

Если вы это сделаете, ReligiousPerson на самом деле не является подклассом Person.

Ответ 3

Нет, это не значит, что структуры не должны использоваться в цепочке наследования (не имеет смысла иметь наследование struct на самом деле наследует другую). То, что вы могли бы сделать, это создать свой собственный класс Struct и реализовать такое же поведение, но сохранить имена атрибутов в унаследованной переменной класса (с class_inheritable_accessor).

Но я не могу понять, почему что-то подобное понадобится.