2015-10-13 3 views
2

я создал два класса, как показано нижерубин Наследование и перезапись класс метод

class Parent 

    def self.inherited(child) 
    child.custom_class_method 
    end 

    def self.custom_class_method 
    raise "You haven't implemented me yet!" 
    end 
end 

class Child < Parent 

    def self.custom_class_method 
    "hello world" 
    end 
end 

Это кажется, что, когда Наследование Child < Parent оценивается, она вызывает self.inherited, что, в свою очередь, повышает версию Parent «s из self.custom_class_method вместо Child. Это проблема, потому что вместо того, чтобы ожидаемый "hello world" я получаю сообщение об ошибке поднял говоря "You haven't implemented me yet!"

Child ли «s self.custom_class_method не получить оценку только после Parent» s self.inherited завершена оценка? И если это так, возможно, работа вокруг этого? Должен ли я просто не поставить raise на родительский класс?

+0

Это кажется странным. Единственный способ получить родительский 'custom_class_method' должен состоять в вызове' super'. В противном случае просто вызванный 'Child.custom_class_method' должен привести к вашему« привет миру ». Не могли бы вы предоставить более глубокие каротажи? –

+0

Хммм Я согласен, что это должно быть! Но кажется, что я могу воспроизвести это, просто скопировав код в консоль «irb». Из-за этой причины он ошибается при оценке класса 'Child'. – aMat

ответ

3

Я думаю, что это следует уточнить:

class Parent 
    def self.inherited(child) 
    puts "Inherited" 
    end 
end 

class Child < Parent 
    puts "Starting to define methods" 
    def self.stuff; end 
end 

Выход становится ясно, что .inherited вызывается в тот момент, вы открываете новый класс, а не при его закрытии. Таким образом, как вы уже догадались, Child.custom_class_method не существует в момент, когда вы пытаетесь его называть - все .inherited видит пустой лист.

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

+0

Ах, умный, я не думал об этом отлаживать! Итак, каков был бы лучший способ определения абстрактных методов здесь? Я просто избегаю ошибок (что мне неудобно, если честно). – aMat

+0

По существу, я пытаюсь создать класс родителя, который может выполнять код в блоке 'self.inherited', который будет немного отличаться от дочернего класса к дочернему. Однако, чтобы гарантировать, что мы не получаем ошибок, я хочу сделать обязательным, чтобы каждый дочерний наследование определял 'custom_method' (отсюда и почему я делал рейз класса Parent). – aMat

+0

Не спрашивайте «как получить дочерний код, выполняемый при наследовании», потому что вы не можете. Кажется, это проблема XY. * Почему * вы хотите, чтобы дочерний код выполнялся при наследовании? '.inherited' должен уведомлять родителей о новых детях, а не запускать код для детей. * Дети * могут запускать код для детей (см. Мои 'puts'). – Amadan

2

Шаблон шаблон/ленивая инициализация может помочь решить проблему. В коде предполагается, что между классами детей отличается информация о соединении с базой данных, возможно, просто имя таблицы или, возможно, совершенно разные базы данных. У родительского класса есть весь код для создания и поддержания соединения, оставляя детей только за то, чтобы предоставлять то, что отличается.

class Parent 
    def connection 
    @connection ||= make_connection 
    end 

    def make_connection 
    puts "code for making connection to database #{database_connection_info}" 
    return :the_connection 
    end 

    def database_connection_info 
    raise "subclass responsibility" 
    end 
end 

class Child1 < Parent 
    def database_connection_info 
    {host: '1'} 
    end 
end 

class Child2 < Parent 
    def database_connection_info 
    {host: '2'} 
    end 
end 

child1 = Child1.new 
child1.connection # => makes the connection with host: 1 
child1.connection # => uses existing connection 

Child2.new.connection # => makes connection with host: 2 
+0

Удивительный материал, спасибо! Как вы думаете, это лучше или хуже, чем абстрагирование этого в модель «DatabaseManager», которая поддерживает эту логику соединения и вызывает «DatabaseManager.make_connection» в каждом классе, а не наследует ее? – aMat

+0

Отличный вопрос. Делегирование против наследования - это старый вопрос. Rails делает упорство через наследование (технически может включать модули вместо этого, но обычно делается через наследование). Многие не считают, что настойчивость должна нести ответственность за класс и идти в другом направлении, например, в шаблоне сопоставления данных (https://en.wikipedia.org/wiki/Data_mapper_pattern). Даже если вы попадаете в лагерь persistence-a-a-ответственности, вы все равно можете абстрагировать поведение базы данных вне класса - возможно, пул соединений. Для этого есть хорошие библиотеки, такие как жемчужина продолжения. –

+0

Я не женат на идее «DatabaseManager», так что это еще одно отличное предложение. Как я уже сказал, мой ответ был неопределенным, поскольку вы знаете о своем проекте намного больше, чем я. Главное, что я пытался сделать, это то, что вы не можете сделать это на 'inherit'. Вы можете сделать это с помощью внешнего менеджера или с унаследованным методом, как здесь, или даже с инициализацией на предке; но «унаследованная» имеет другую цель. – Amadan

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