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

1.3.4. Создание классов

1.3.4. Создание классов

В Ruby есть множество встроенных классов, и вы сами можете определять новые. Для определения нового класса применяется такая конструкция:

class ClassName
# ...
end

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

Попутное замечание: строго говоря, классы в Ruby не имеют имен. «Имя» класса — это всего лишь константа, ссылающаяся на объект типа Class (поскольку в Ruby Class — это класс). Ясно, что на один и тот же класс могут ссылаться несколько констант, и их можно присваивать переменным точно так же, как мы поступаем с любыми другими объектами (поскольку в Ruby Class — это объект). Если вы немного запутались, не расстраивайтесь. Удобства ради новичок может считать, что в Ruby имя класса — то же самое, что в C++.

Вот как определяется простой класс:

class Friend
 @@myname = "Эндрю"           # переменная класса
 def initialize(name, sex, phone)
  @name, @sex, @phone = name, sex, phone
  # Это переменные экземпляра
 end
 def hello # метод экземпляра
  puts "Привет, я #{@name}."
 end
 def Friend.our_common_friend # метод класса
  puts "Все мы друзья #{@@myname}."
 end
end
f1 = Friend.new("Сюзанна","F","555-0123")
f2 = Friend.new("Том","M","555-4567")
f1.hello                      # Привет, я Сюзанна.
f2.hello                      # Привет, я Том.
Friend.our_common_friend      # Все мы друзья Эндрю.

Поскольку данные уровня класса доступны во всем классе, их можно инициализировать в момент определения класса. Если определен метод с именем initialize, то гарантируется, что он будет вызван сразу после выделения памяти для объекта. Этот метод похож на традиционный конструктор, но не выполняет выделения памяти. Память выделяется методом new, а освобождается неявно сборщиком мусора.

Теперь взгляните на следующий фрагмент, обращая особое внимание на методы getmyvar, setmyvar и myvar=:

class MyClass
 NAME = "Class Name"  # константа класса
 @@count = 0          # инициализировать переменную класса
 def initialize       # вызывается после выделения памяти для объекта
  @@count += 1
  @myvar = 10
 end
 def MyClass.getcount # метод класса
  @@count             # переменная класса
 end
 def getcount         # экземпляр возвращает переменную класса!
  @@count             # переменная класса
 end
 def getmyvar         # метод экземпляра
  @myvar              # переменная экземпляра
 end
 def setmyvar(val)    # метод экземпляра устанавливает @myvar
  @myvar = val
 end
 def myvar=(val)      # другой способ установить @myvar
  @myvar = val
 end
end
foo = MyClass.new # @myvar равно 10
foo.setmyvar 20 # @myvar равно 20
foo.myvar =30 # @myvar равно 30

Здесь мы видим, что getmyvar возвращает значение переменной @myvar, а setmyvar устанавливает его. (Многие программисты говорят о методах чтения и установки). Все это работает, но не является характерным способом действий в Ruby. Метод myvar= похож на перегруженный оператор присваивания (хотя, строго говоря, таковым не является); это более удачная альтернатива setmyvar, но есть способ еще лучше.

Класс Module содержит методы attr, attr_accessor, attr_reader и attr_writer. Ими можно пользоваться (передавая символы в качестве параметров) для автоматизации управления доступом к данным экземпляра. Например, все три метода getmyvar, setmyvar и myvar= можно заменить одной строкой в определении класса:

attr_accessor :myvar

При этом создается метод myvar, который возвращает значение @myvar, и метод myvar=, который позволяет изменить значение той же переменной. Методы attr_reader и attr_writer создают соответственно версии методов доступа к атрибуту для чтения и для изменения.

Внутри методов экземпляра, определенных в классе, можно при необходимости пользоваться переменной self. Это просто ссылка на объект, от имени которого вызван метод экземпляра.

Для управления видимостью методов класса можно пользоваться модификаторами private, protected и public. (Переменные экземпляра всегда закрыты, обращаться к ним извне класса можно только с помощью методов доступа.) Каждый модификатор принимает в качестве параметра символ, например :foo, а если он опущен, то действие модификатора распространяется на все последующие определения в классе. Пример:

class MyClass
 def method1
  # ...
 end
 def method2
  # ...
 end
 def method3
  # ...
 end
 private :method1
 public
 :method2
 protected :method3
 private
 def my_method
  # ...
 end
 def another_method
  # ...
 end
end

В этом классе метод method1 закрытый, method2 открытый, a method3 защищенный. Поскольку далее вызывается метод private без параметров, то методы my_method и another_method будут закрытыми.

Уровень доступа public не нуждается в объяснениях, он не налагает никаких ограничений ни на доступ к методу, ни на его видимость. Уровень private означает, что метод доступен исключительно внутри класса или его подклассов и может вызываться только в «функциональной форме» от имени self, причем вызывающий объект может указываться явно или подразумеваться неявно. Уровень protected означает, что метод вызывается только внутри класса, но, в отличие от закрытого метода, не обязательно от имени self.

По умолчанию все определенные в классе методы открыты. Исключение составляет лишь initialize. Методы, определенные на верхнем уровне программы, тоже по умолчанию открыты. Если они объявлены закрытыми, то могут вызываться только в функциональной форме (как, например, методы, определенные в классе Object).

Классы в Ruby сами являются объектами — экземплярами метакласса Class. Классы в этом языке всегда конкретны, абстрактных классов не существует. Однако теоретически можно реализовать и абстрактные классы, если вам это для чего-то понадобится.

Класс Object является корнем иерархии. Он предоставляет все методы, определенные во встроенном модуле Kernel.

Чтобы создать класс, наследующий другому классу, нужно поступить следующим образом:

class MyClass < OtherClass
 # ...
end

Помимо использования встроенных методов, вполне естественно определить и собственные либо переопределить унаследованные. Если определяемый метод имеет то же имя, что и существующий, то старый метод замещается. Если новый метод должен обратиться к замещенному им «родительскому» методу (так бывает часто), можно воспользоваться ключевым словом super.

Перегрузка операторов, строго говоря, не является неотъемлемой особенностью ООП, но этот механизм знаком программистам на C++ и некоторых других языках. Поскольку большинство операторов в Ruby так или иначе являются методами, то не должен вызывать удивления тот факт, что их можно переопределять или определять в пользовательских классах. Переопределять семантику оператора в существующем классе редко имеет смысл, зато в новых классах определение операторов — обычное дело.

Можно создавать синонимы методов. Для этого внутри определения класса предоставляется такой синтаксис:

alias newname oldname

Число параметров будет таким же, как для старого имени, и вызываться метод-синоним будет точно так же. Обратите внимание на отсутствие запятой; alias — это не имя метода, а ключевое слово. Существует метод с именем alias_method, который ведет себя аналогично, но в случае его применения параметры должны разделяться запятыми, как и для любого другого метода.

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


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