Ruby Объектно-ориентированный

Ruby — это чистый объектно-ориентированный язык, и в Ruby всё принимает форму объектов. Каждое значение в Ruby является объектом, даже самые примитивные вещи: строки, числа, и даже true и false являются объектами. Сам класс также являетсяобъектом, который являетсяClassэкземпляром класса Class. В этой главе будут объяснены все основные возможности, связанные с объектно-ориентированным программированием на Ruby.

Классы используются для задания формы объектов. Они объединяют представление данных и методы, организуя данные в аккуратный пакет. Данные и методы в классе называются членами класса.

Определение класса Ruby

Когда вы определяете класс, вы фактически определяете шаблон для типа данных. Это фактически не определяет никаких данных, а скорее определяет, что означает имя класса, то есть из чего будут состоять объекты класса и какие операции можно выполнять с объектом.

Определение класса начинается с ключевого словаclass, за которым следуетимя класса, и наконец заканчиваетсяendразделитель для обозначения конца определения класса. Например, мы используем ключевое слово class для определения класса Box, как показано ниже:

class Box code end

По соглашению имя должно начинаться с заглавной буквы. Если оно содержит несколько слов, пишите первую букву каждого слова заглавной, но без разделителей между ними (например, CamelCase).

Определение объектов Ruby

Классы предоставляют шаблон для объектов, поэтому по сути объекты создаются на основе класса. Мы используемnewкак ключевое слово для объявления объектов класса. Следующий оператор объявляет два объекта класса Box:

box1 = Box.new box2 = Box.новый

Метод initialize

initializeМетод является стандартным методом класса Ruby, служащим конструктором класса, и аналогичен принципу работыконструкторав других объектно-ориентированных языках программирования. Когда вы хотите инициализировать некоторые переменные класса при создании объекта, метод initialize пригодится. Этот метод принимает ряд параметров; как и другие методы Ruby, при его использовании вы должны поставитьdefключевое слово перед ним, как показано ниже:

class Box def initialize(w,h) @width, @height = w, h end end

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

Переменные экземпляраявляются атрибутами класса. Когда объекты создаются с помощью класса, они становятся атрибутами объектов. Атрибуты каждого объекта назначаются отдельно и не разделяют значения с другими объектами. Внутри класса эти атрибуты доступны с помощью оператора @; вне класса доступ к ним осуществляется с помощью методов, называемыхметодами доступа, которые относятсякpublicметодам. Ниже мы возьмём класс, определённый выше,Boxв качестве примера и используем @width и @height как переменные экземпляра класса Box.

class Box def initialize(w,h) #Присваивание значений переменным экземпляра @width, @height = w, h end end

Методы доступа (getter) и установки (setter)

Чтобы читать переменные, определённые в классе, извне класса, мы можем определить методы доступа (getter) для доступа к ним. В следующем примере демонстрируется использование методов доступа:

Пример

#!/usr/bin/ruby -w #Определите класс class Box #Конструктор def initialize(w,h) @width, @height = w, h end #Методы доступа def printWidth @width end def printHeight @height end end #Создайте объект и инициализируйте высоту и ширину box box = Box.new(10, 20) #Используйте методы доступа x = box.printWidth() y = box.printHeight() puts "Ширина Box : #{x}" puts "Высота Box : #{y}"

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

盒子宽度 : 10
盒子高度 : 20

Подобно методам доступа, используемым для доступа к значениям переменных, Ruby предоставляет способ передавать параметры переменным, определённым в классе, извне класса, так называемыеметоды установки, определяемые следующим образом:

Пример

#!/usr/bin/ruby -w #Определите класс class Box #Метод-конструктор def initialize(w,h) @width, @height = w, h end #Методы доступа def getWidth @width end def getHeight @height end #Методы установки def setWidth=(value) @width = value end def setHeight=(value) @height = value end end #Создание объектов box = Box.new(10, 20) #Использование методов-сеттеров box.setWidth = 30 box.setHeight = 50 #Использование методов-аксессоров x = box.getWidth() y = box.getHeight() puts "Ширина коробки : #{x}" puts "Высота Box : #{y}"

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

盒子宽度 : 30
盒子高度 : 50

Поскольку оба метода очень часто используются, Ruby определяетattr_accessor :variable_name、attr_reader :variable_name、attr_writer :variable_nameтри метода объявления атрибутов. Среди них:accessor=reader+writer。

Также обратите внимание: перед именем переменной должен быть префикс:, а между именами переменных используйте,для разделения.

Методы экземпляра

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

Пример

#!/usr/bin/ruby -w #Определите класс class Box #Метод-конструктор def initialize(w,h) @width, @height = w, h end #Метод экземпляра def getArea @width * @height end end #Создание объектов box = Box.new(10, 20) #Вызов метода экземпляра a = box.getArea() puts "Площадь коробки равна: #{a}"

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

Area of the box is : 200

Методы класса & переменные класса

Переменные класса— это переменные, общие для всех экземпляров класса. Иными словами, переменные класса доступны всем экземплярам объектов. Переменные класса имеют префикс из двух символов @ (@@). Переменные класса должны быть инициализированы в определении класса, как показано в примере ниже.

Методы классаопределяются с помощью ключевого словаdef self.methodname(), и методы класса заканчиваются разделителем end. Методы класса можно вызывать, используя формуclassname.methodnameс именем класса, как показано в примере ниже:

Пример

#!/usr/bin/ruby -w class Box #Инициализировать переменные класса @@count = 0 def initialize(w,h) #Присвоить значения переменным экземпляра @width, @height = w, h @@count += 1 end def self.printCount() puts "Количество Box: #@@count" end end #Создать два объекта box1 = Box.new(10, 20) box2 = Box.new(30, 100) #Вызвать метод класса для вывода количества коробок Box.printCount()

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

Box count is : 2

Метод to_s

Любой определяемый вами класс имеет экземплярный методto_s, возвращающий строковое представление объекта. Ниже приведён простой пример, представляющий объекты Box на основе ширины и высоты:

Пример

#!/usr/bin/ruby -w class Box #Метод-конструктор def initialize(w,h) @width, @height = w, h end #Определите метод to_s def to_s "(w:#@width,h:#@height)" #Строковый формат объекта end end #Создание объектов box = Box.new(10, 20) #Метод to_s вызывается автоматически puts "Строковое представление box: #{box}"

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

String representation of box is : (w:10,h:20)

Управление доступом

Ruby предоставляет вам три уровня защиты методов экземпляра, а именноpublic, private, or protected. Ruby не применяет контроль доступа к переменным экземпляра и класса.

  • Публичные методы:Публичные методы могут быть вызваны любым объектом. По умолчанию методы являются публичными, кроме метода initialize, который всегда приватный.
  • Приватные методы:Приватные методы не могут быть доступны или просмотрены извне класса. Только методы класса могут получать доступ к приватным членам.
  • Защищённые методы:Защищённые методы могут вызываться только объектами класса и его подклассов. Доступ также может быть осуществлён только внутри класса и его подклассов.

Ниже приведён простой пример, демонстрирующий синтаксис этих трёх модификаторов:

Пример

#!/usr/bin/ruby -w #Определите класс class Box #Метод-конструктор def initialize(w,h) @width, @height = w, h end #Методы экземпляра по умолчанию public def getArea getWidth() * getHeight end #Определите private методы доступа def getWidth @width end def getHeight @height end # сделайте их private private :getWidth, :getHeight #Метод экземпляра для вывода площади def printArea @area = getWidth() * getHeight puts "Площадь большой коробки: #@area" end #Сделайте метод экземпляра protected protected :printArea end #Создайте объекты box = Box.new(10, 20) #Вызовите методы экземпляра a = box.getArea() puts "Площадь коробки равна: #{a}" #Попробуйте вызвать защищённый метод экземпляра box.printArea()

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат. Здесь первый вызов метода завершается успешно, но второй метод вызывает проблему.

Area of the box is : 200
test.rb:42: protected method `printArea' called for #
<Box:0xb7f11280 @height=20, @width=10> (NoMethodError)

Наследование классов

Наследование — одна из наиболее важных концепций объектно-ориентированного программирования. Наследование позволяет определять класс на основе другого класса, что упрощает создание и поддержку приложений.

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

При создании класса программист может напрямую указать, что новый класс наследует члены существующего класса, избегая необходимости писать новые элементы данных и функции-члены с нуля. Этот существующий класс называетсябазовым классом или родительским классом, а новый класс называетсяпроизводным классом или подклассом。

Ruby также предоставляет концепцию создания подклассов, которая является наследованием. Следующий пример объясняет эту концепцию. Синтаксис расширения класса очень прост. Просто добавьте символ < и имя родительского класса в инструкцию class. Например, следующее определяет классBigBoxДаBoxподкласс:

Пример

#!/usr/bin/ruby -w #Определить класс class Box #Метод-конструктор def initialize(w,h) @width, @height = w, h end #Метод экземпляра def getArea @width * @height end end #Определить подкласс class BigBox < Box #Добавить новый метод экземпляра def printArea @area = @width * @height puts "Площадь большого прямоугольника равна : #@area" end end #Создать объект box = BigBox.new(10, 20) #Вывести площадь box.printArea()

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

Big box area is : 200

Перегрузка методов

Хотя в производном классе можно добавить новую функциональность, иногда может потребоваться изменить поведение метода, уже определённого в родительском классе. В этом случае можно оставить имя метода без изменений и переопределить функциональность метода, как показано в следующем примере:

Пример

#!/usr/bin/ruby -w #Определить класс class Box #Метод-конструктор def initialize(w,h) @width, @height = w, h end #Метод экземпляра def getArea @width * @height end end #Определить подкласс class BigBox < Box #Изменить существующий метод getArea def getArea @area = @width * @height puts "Площадь большого прямоугольника равна : #@area" end end #Создать объект box = BigBox.new(10, 20) #Использовать переопределённый метод для вывода площади box.getArea()

Попробуйте »

Вывод приведенного выше примера:

Big box area is : 200

Перегрузка операторов

Мы хотим использовать оператор + для выполнения векторного сложения двух объектов Box, использовать оператор * для умножения ширины и высоты Box, а также использовать унарный оператор - для отрицания ширины и высоты Box. Ниже приведена версия класса Box с определениями математических операторов:

class Box def initialize(w,h) #Инициализировать ширину и высоту @width,@height = w, h end def +(other) #Определить + для выполнения векторного сложения Box.new(@width + other.width, @height + other.height) end def -@ #Определить унарный оператор - для отрицания ширины и высоты Box.new(-@width, -@height) end def *(scalar) #Выполнить скалярное умножение Box.new(@width*scalar, @height*scalar) end end

Замораживание объектов

Иногда мы хотим предотвратить изменение объектов. В классе Object метод freeze может этого добиться, фактически превращая объект в константу. Любой объект можно заморозить, вызвавObject.freezefreeze. Замороженный объект нельзя изменить, то есть вы не можете изменять его переменные экземпляра.

Вы можете использоватьObject.frozen?метод, чтобы проверить, был ли заморожен данный объект. Если объект заморожен, этот метод возвращает true; в противном случае он возвращает false. Следующий пример поясняет эту концепцию:

Пример

#!/usr/bin/ruby -w #Определить класс class Box #Метод-конструктор def initialize(w,h) @width, @height = w, h end #Метод доступа def getWidth @width end def getHeight @height end #Метод установки def setWidth=(value) @width = value end def setHeight=(value) @height = value end end #Создать объект box = Box.new(10, 20) #Давайте заморозим объект box.freeze if( box.frozen? ) puts "Объект Box — замороженный объект" else puts "Объект Box — обычный объект" end #Теперь попробуйте использовать метод-установщик box.setWidth = 30 box.setHeight = 50 #Используйте метод доступа x = box.getWidth() y = box.getHeight() puts "Ширина коробки: #{x}" puts "Высота коробки: #{y}"

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

Box object is frozen object
test.rb:20:in `setWidth=': can't modify frozen object (TypeError)
        from test.rb:39

Константы класса

Вы можете определить константу внутри класса, присвоив переменной непосредственно числовое или строковое значение. Константы не нуждаются в использовании @ или @@. По соглашению имена констант пишутся с заглавной буквы.

После того как константа определена, вы не можете изменить её значение. Вы можете получить доступ к константе непосредственно внутри класса, так же как к переменной, но если вы хотите получить доступ к константе вне класса, вы должны использоватьclassname::constant::, как показано в следующем примере.

Пример

#!/usr/bin/ruby -w #Определить класс class Box BOX_COMPANY = "TATA Inc" BOXWEIGHT = 10 #Метод-конструктор def initialize(w,h) @width, @height = w, h end #Метод экземпляра def getArea @width * @height end end #Создать объект box = Box.new(10, 20) #Вызов метода экземпляра a = box.getArea() puts "Площадь коробки равна: #{a}" puts Box::BOX_COMPANY puts "Вес коробки: #{Box::BOXWEIGHT}"

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

Area of the box is : 200
TATA Inc
Box weight is: 10

Константы класса могут наследоваться, а также могут быть переопределены, как методы экземпляра.

Создание объектов с помощью allocate

Может возникнуть ситуация, когда вы хотите создать объект, не вызывая конструктор объектаinitialize, т. е. не используя метод new для создания объекта. В этом случае вы можете вызвать allocate, чтобы создать неинициализированный объект, как показано в следующем примере:

Пример

#!/usr/bin/ruby -w #Определить класс class Box attr_accessor :width, :height #Метод-конструктор def initialize(w,h) @width, @height = w, h end #Метод экземпляра def getArea @width * @height end end #Создание объекта с помощью new box1 = Box.new(10, 20) #Создание другого объекта с помощью allocate box2 = Box.allocate #Вызов метода экземпляра с помощью box1 a = box1.getArea() puts "Площадь коробки: #{a}" #Вызов метода экземпляра с помощью box2 a = box2.getArea() puts "Площадь коробки: #{a}"

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

Area of the box is : 200
test.rb:14: warning: instance variable @width not initialized
test.rb:14: warning: instance variable @height not initialized
test.rb:14:in `getArea': undefined method `*' 
   for nil:NilClass (NoMethodError) from test.rb:29

Информация о классе

self в Ruby и this в Java имеют сходства, но также сильно различаются. В Java методы вызываются внутри методов экземпляра, поэтому this обычно указывает на текущий объект. Код Ruby выполняется строка за строкой, поэтому self имеет разные значения в разных контекстах. Давайте посмотрим на следующий пример:

Пример

#!/usr/bin/ruby -w class Box #Вывод информации о классе puts "Класс self = #{self.class}" puts "Имя self = #{self.name}" end

Попробуйте »

Когда приведённый выше код выполняется, он выдаёт следующий результат:

Class of self = Class
Name of self = Box

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

Другие расширения