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)