Книга: Программирование на языке Ruby

11.1.5. Наследование суперклассу

11.1.5. Наследование суперклассу

Можно унаследовать класс, воспользовавшись символом <:

class Boojum < Snark
 # ...
end

Это объявление говорит, что класс Boojum является подклассом класса Snark или — что то же самое — класс Snark является суперклассом класса Boojum. Всем известно, что каждый буюм является снарком, но не каждый снарк — буюм.

Ясно, что цель наследования — расширить или специализировать функциональность. Мы хотим получить из общего нечто более специфическое.

Попутно отметим, что во многих языках, например в C++, допускается множественное наследование (МН). В Ruby, как и в Java, и в некоторых других языках, множественного наследования нет, но наличие классов-примесей компенсирует его отсутствие (см. раздел 11.1.12).

Рассмотрим несколько более реалистичный пример. У нас есть класс Person (человек), а мы хотим создать производный от него класс Student (студент).

Определим класс Person следующим образом:

class Person
 attr_accessor :name, :age, :sex
 def initialize(name, age, sex)
  @name, @age, @sex = name, age, sex
 end
 # ...
end

А класс Student — так:

class Student < Person
 attr_accessor :idnum, :hours
 def initialize(name, age, sex, idnum, hours)
  super(name, age, sex)
  @idnum = idnum
  @hours = hours
 end
 # ...
end
# Создать два объекта.
a = Person.new("Dave Bowman", 37, "m")
b = Student.new("Franklin Poole", 36, "m", "000-13-5031", 24)

Посмотрим внимательно, что здесь сделано. Что за super, вызываемый из метода initialize класса Student? Это просто вызов соответствующего метода родительского класса. А раз так, то ему передается три параметра (хотя наш собственный метод initialize принимает пять).

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

Если говорить об истинном смысле наследования, то оно, безусловно, описывает отношение «является». Студент является человеком, как и следовало ожидать. Сделаем еще три замечания:

• Каждый атрибут (и метод) родительского класса отражается в его потомках. Если в классе Person есть атрибут height, то класс Student унаследует его, а если родитель имеет метод say_hello, такой метод будет и у потомка.

• Потомок может иметь дополнительные атрибуты и методы, мы это только что видели. Поэтому создание подкласса часто еще называют расширением суперкласса.

• Потомок может переопределять любые атрибуты и методы своего родителя.

Последнее замечание подводит нас к вопросу о том, как разрешается вызов метода. Откуда я знаю, вызывается ли метод конкретного класса или его суперкласса?

Краткий ответ таков: не знаю и не интересуюсь. Если вызывается некий метод от имени объекта класса Student, то будет вызван метод, определенный в этом классе, если он существует. А если нет, вызывается метод суперкласса и так далее вверх по иерархии наследования. Мы говорим «и так далее», потому что у каждого класса (кроме Object) есть суперкласс.

А что если мы хотим вызвать метод суперкласса, но не из соответствующего метода подкласса? Можно сначала создать в подклассе синоним:

class Student # Повторное открытие класса.
 # Предполагается, что в классе Person есть метод say_hello...
 alias :say_hi :say_hello
 def say_hello
  puts "Привет."
 end
 def formal_greeting
  # Поприветствовать так, как принято в суперклассе.
  say_hi
 end
end

У наследования есть разные тонкости, которых мы здесь касаться не будем. Общий принцип мы изложили, но не пропустите следующий раздел.

Оглавление книги


Генерация: 1.270. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз