KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Хэл Фултон - Программирование на языке Ruby

Хэл Фултон - Программирование на языке Ruby

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Хэл Фултон, "Программирование на языке Ruby" бесплатно, без регистрации.
Перейти на страницу:

 MAX_SAMPLE = 192


 def SoundPlayer.detect_hardware

  # ...

 end


 def play

  # ...

 end

end

Есть еще один способ объявить этот метод класса. В следующем фрагменте делается практически то же самое:

class SoundPlayer


MAX_SAMPLE =192


 def play

  # ...

 end


end


def SoundPlayer.detect_hardware

 # ...

end

Единственная разница касается использования объявленных в классе констант. Если метод класса объявлен вне объявления самого класса, то эти константы оказываются вне области видимости. Например, в первом фрагменте метод detect_hardware может напрямую обращаться к константе MAX_SAMPLE, а во втором придется пользоваться нотацией SoundPlayer::MAX_SAMPLE.

Не удивительно, что помимо методов класса есть еще и переменные класса. Их имена начинаются с двух знаков @, а областью видимости является весь класс, а не конкретный его экземпляр.

Традиционный пример использования переменных класса - подсчет числа его экземпляров. Но они могут применяться всегда, когда информации имеет смысл в контексте класса в целом, а не отдельного объекта. Другой пример приведен в листинге 11.3.

Листинг 11.3. Переменные и методы класса

class Metal


 @@current_temp = 70


 attr_accessor :atomic_number


 def Metal.current_temp=(x)

  @@current_temp = x

 end


 def Metal.current_temp

  @@current_temp

 end


 def liquid?

  @@current_temp >= @melting

 end


 def initialize(atnum, melt)

  @atomic_number = atnum

  @melting = melt

 end


end


aluminum = Metal.new(13, 1236)

copper = Metal.new(29, 1982)

gold = Metal.new(79, 1948)


Metal.current_temp = 1600


puts aluminum.liquid? # true

puts copper.liquid?   # false

puts gold.liquid?     # false


Metal.current_temp = 2100


puts aluminum.liquid? # true

puts copper.liquid?   # true

puts gold.liquid?     # true

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

А если попытаться, что произойдет? Что если мы попробуем напечатать атрибут @atomic_number из метода Metal.current_temp? Обнаружится, что переменная вроде бы существует — никакой ошибки не возникает, — но имеет значение nil. В чем дело?

В том, что на самом деле мы обращаемся вовсе не к переменной экземпляра класса Metal, а к переменной экземпляра класса Class. (Напомним, что в Ruby Class — это класс!)

Мы столкнулись с переменной экземпляра класса (термин заимствован из языка Smalltalk). Дополнительные замечания на эту тему приводятся в разделе 11.2.4.

В листинге 11.4 иллюстрируются все аспекты этой ситуации.

Листинг 11.4. Данные класса и экземпляра

class MyClass


 SOME_CONST = "alpha"     # Константа уровня класса.


 @@var = "beta"           # Переменная класса.

 @var = "gamma"           # Переменная экземпляра класса.


 def initialize

  @var = "delta"          # Переменная экземпляра.

 end


 def mymethod

  puts SOME_CONST         # (Константа класса.)

  puts @@var              # (Переменная класса.)

  puts @var               # (Переменная экземпляра.)

 end


 def MyClass.classmeth1

  puts SOME_CONST         # (Константа класса.)

  puts @@var              # (Переменная класса.)

  puts @var               # (Переменная экземпляра класса.)

 end


end


def MyClass.classmeth2

 puts MyClass::SOME_CONST # (Константа класса.)

 # puts @@var             # Ошибка: вне области видимости.

 puts @var                # (Переменная экземпляра класса.)

end


myobj = MyClass.new

MyClass.classmeth1        # alpha, beta, gamma

MyClass.classmeth2        # alpha, gamma

myobj.mymethod            # alpha, beta, delta

Следует еще сказать, что метод класса можно сделать закрытым, воспользовавшись методом private_class_method. Это аналог метода private на уровне экземпляра. См. также раздел 11.2.10.

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

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

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*