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

11.1.4. Создание атрибутов и методов уровня класса

11.1.4. Создание атрибутов и методов уровня класса

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

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

В следующем далеко не полном фрагменте предполагается, что мы создаем класс для проигрывания звуковых файлов. Метод play естественно реализовать как метод экземпляра, ведь можно создать много объектов, каждый из которых будет проигрывать свой файл. Но у метода detect_hardware контекст более широкий; в зависимости от реализации может оказаться, что создавать какие-либо объекты вообще не имеет смысла, если этот метод возвращает ошибку. Следовательно, его контекст — вся среда воспроизведения звука, а не конкретный звуковой файл.

class SoundPlayer
 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.

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


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