Выбрать главу

     def fly

           puts "Все представители класса #{self.class} умеют летать!"

     end

end

class Penguin < Bird def fly

puts "Представители класса #{self.class} не летают…" end end

b = Bird.new; b.lay_egg; b.fly p = Penguin.new; p.lay_egg; p.fly

Создание класса

Дальнейшее описание проиллюстрируем обыкновенными дробями. В процессе вычислений нередко возникает необходимость работы с дробными числами. Учитывая ограничения по точности представления чисел с плавающей точкой в компьютере, иногда бывает необходимо производить вычисления в терминах обыкновенных дробей, представляющих собой отношение целого числителя и целого знаменателя. В Ruby имеется класс Rational, описывающий такие дроби. Представим на время, что его нет и создадим нечто подобное.

classFrac

     definitialize(a,b)

           raise’Divisionbyzero’ifb.to_i == 0

           @numerator,@denominator = a.to_i, b.to_i

     end

end

fr=Frac.new(1,2)

Метод initialize называется конструктором класса. Он вызывается каждый раз, когда мы создаем экземпляр класса при помощи метода new.

Конструкция raise вызывает исключительную ситуацию (об этом будет рассказано позже) и выдает соответствующее сообщение об ошибке.

Переменные экземпляра

Переменные enumerator, @denominator – это переменные атрибуты экземпляра класса. В именах переменных экземпляра необходимо использовать префикс @. Каждый объект, принадлежащий данному классу, имеет свои собственные значения этих атрибутов (свойства), но пока их можно использовать только внутри методов самого класса. При попытке обратиться к таким переменным извне класса будет выдано сообщение об ошибке. Что же делать, если хочется иметь доступ к переменным экземпляра вне класса?

Один способ состоит в создании методов, возвращающих значение соответствующего атрибута. Но в случае большого числа атрибутов такой подход не удобен. Язык Ruby предлагает более удобную возможность.

Для контроля доступа к переменным экземпляра можно использовать макросы attr_reader (для чтения), attr_writer (для изменения значения) и attr_accessor (для выдачи разрешения на оба действия).

Метод to_s, присутствующий в классе, определяет, как объект должен отображаться при печати.

Переопределение операторов

Оперирование дробями предполагает возможность производить математические действия, например, сложение. Символ + является оператором языка Ruby. Но некоторые операторы для удобства пользователя могут быть переопределены. Но не все! Например, + может быть переопределен, = нет.

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

Модификация и создание пользовательских классов

class Frac

def initialize(a, b)

           raise ’Division by zero’ if b.to_i == 0

           @numerator,@denominator = a.to_i, b.to_i

     simplify()

     end

end

fr=Frac.new(1,2)

     def +(b)

if b.class != Frac

           raise "Undefined method + for class Frac and #{b.class}!"

end

return Frac.new(self.numerator * b.denominator + b.numerator * self.denominator,

     self.denominator * b.denominator)

end

private

def simplify()

     x, y = @numerator.abs, @denominator.abs x, y = y, x % y while x * y > 0

     m = x + y

@numerator, @denominator = @numerator / m, @denominator / m

     end

end

В операторе сложения мы проверяем, что второй аргумент операции + тоже является объектом типа Frac.

Для каждого объекта в Ruby можно применить метод class, чтобы выяснить, к какому классу он относится. Более правильно проверять, что объект относится к тому или иному классу, при помощи метода kind_of?.

Ключевое слово self указывает на объект, вызвавший метод. Запись self.numerator эквивалентна enumerator.

Метод simplify реализует алгоритм Евклида, упрощающий дроби. Чтобы у нас всегда были упрощенные дроби, он вызывается в конструкторе класса. Ключевое слово private определяет право доступа к следующим за ним методам только внутри класса. Таким образом, метод simplify не может быть использован вне класса Frac.

Аналогично + мы можем также переопределять и логические операторы. Например, оператор ==, позволяющий сравнивать два объекта на равество.

class Frac def ==(b)

     if !b.kind_of?(Frac)

           raise "Undefined method == for class Frac and #{b.class}!" end

     return (self.numerator == b.numerator and self.denominator == b.denominator)