Object Oriented Ruby

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

Класс используется для определения формы объекта и объединяет представление данных и методы для управления этими данными в один аккуратный пакет. Данные и методы внутри класса называются членами класса. Класс — в объектно-ориентированном программировании, представляет собой шаблон для создания объектов, обеспечивающий начальные значения состояний: инициализация полей-переменных и реализация поведения функций или методов.

Определение класса Ruby (Ruby class definition):

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

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

class Box

...code....

end

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

Определение объектов в Ruby (Define ruby objects):

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

box1 = Box.new

box2 = Box.new

Метод инициализации (The initialize method):

Метод инициализации initialize method это стандартный методо класса Ruby и работает почти так же, как конструктор в других ООП языках. Метод инициализации полезен, когда вы хотите инициализировать некоторые переменные класса во время создания объекта. Этот метод может принимать список параметров и, как любому другому методу Ruby, ему должно предшествовать ключевое слово def (define-определять), как показано ниже:

class Box

def initialize(w, h)

@width, @height = w, h

end

end

Переменные экземпляра (The instance variable):

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

class Box

def initialize(w, h)

# присваиваем экземпляру переменную

@width, @height = w, h

end

end

Методы доступа и установки (The accessor & setter methods):

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

#!/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.new(10, 20)

# использовать методы доступа

x = box.printWidth()

y = box.printHeight()

puts "Width of the box is: #{x}"

puts "Height of the box is: #{y} "

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

Width of the box is: 10

Height of the box is: 20

Подобно методам доступа, которые используются для доступа к значениям переменных, Ruby предоставляет способ установки значений этих переменных извне класса с помощью методов установки (setter methods), которые определены ниже:

#!/usr/bin/ruby ​​-w

# определение класса

class Box

# метод конструктора (constructor method)

def initialize(w, h)

@width, @height = w, h

end

# методы доступа (accessor methods)

def getWidth

@width

end

def getHeight

@height

end

# методы установки (setter methods)

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 "Width of the box: #{x}"

puts "Height of the box: #{y}"

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

Ширина ящика: 30

Высота ящика: 50

Методы экземпляра (The instance methods):

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

#!/usr/bin/ruby ​​-w

# определить класс

class Box

# метод конструктора (constructor method)

def initialize(w, h)

@width, @height = w, h

end

# метод экземпляра (instance methods)

def getArea

@width * @height

end

end


# создать an object

box = Box.new(10, 20)

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

a = box.getArea()

puts "Площадь ящика: #{a}"

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

Площадь ящика: 200

Методы и переменные класса (The class methods & variables):

Переменные класса (class variables) - это переменная, которая используется всеми экземплярами класса. Другими словами, существует один экземпляр переменной, и к нему обращаются экземпляры объекта. Переменные класса начинаются с двух символов @ (@@). Переменная класса должна быть инициализирована в определении класса, как показано ниже.

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

class Box

# Инициализируем переменные нашего класса

@@count = 0

def initialize(w, h)

# assing instance variables

@width, @height = w, h


@@count += 1

end

def self.printCount()

puts "Box count is : #@@count"

end

end

# создать два объекта

box1 = Box.new(10, 20)

box2 = Box.new(30, 100)

# вызвать метод класса для печати количества боксов

Box.printCount()

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

Box count is : 2

Метод to_s (The to_s method):

Любой определяемый вами класс должен иметь метод экземпляра to_s для возврата строкового представления объекта. Следуя простому примеру для представления объекта Box с точки зрения ширины и высоты:

#!/usr/bin/ruby ​​-w

class Box

# метод конструктора (constructor method)

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}"

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

Строковое представление поля: (w: 10, h: 20)

Контроль доступа (Access Control):

Ruby предоставляет вам три уровня защиты на уровне методов экземпляра, эти уровни могут быть публичными, частными или защищенными. Ruby не применяет никакого контроля доступа к переменным экземпляра и класса.

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

  • Частные методы: закрытые методы недоступны или даже просмотрены извне класса. Только методы класса могут получить доступ к закрытым членам.

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

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

#!/usr/bin/ruby ​​-w

# определить класс (define a class)

class Box

# метод конструктора (constructor method)

def initialize(w, h)

@width, @height = w, h

end

# метод экземпляра по умолчанию это public

def getArea

getWidth() * getHeight

end

# определить частные методы доступа

def getWidth

@width

end

def getHeight

@height

end

# сделать их частными

private :getWidth, :getHeight


# метод экземпляра для печати области

def printArea

@area = getWidth () * getHeight

puts "Область большого ящика: #@area "

end

# сделать его защищенным

protected :printArea

end


# создать объект

box = Box.new(10, 20)

# вызвать методы экземпляра

a = box.getArea()

puts "Area of ​​the box is: #{a} "

# попытаться вызвать защищенные методы

box.printArea()

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

Area of the box is: 200

test.rb: 42: protected method 'printArea' , вызываемый для #

<Box: 0xb7f11280 @ height = 20, @ width = 10> (NoMethodError)

Наследование класса (Class Inheritance):

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

Наследование также дает возможность повторно использовать функциональность кода и быстрое время реализации, но, к сожалению, Ruby не поддерживает множественные уровни наследования, но Ruby поддерживает миксины (mixins - примесь от слова mix). Примесь похожа на специализированную реализацию множественного наследования, в которой наследуется только часть интерфейса.

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

Ruby также поддерживает концепцию создания подклассов, т.е. наследование и следующий пример объясняют концепцию. Синтаксис расширения класса прост. Просто добавьте <и имя суперкласса в оператор класса.

Например, следующее определение класса BigBox как подкласса Box:

#!/usr/bin/ruby ​​-w

# определить класс (define a class)

class Box

# метод конструктора (constructor method)

def initialize(w, h)

@width, @height = w, h

end

# экземпляр метода (instance method)

def getArea

@width * @height

end

end


# определить подкласс

class BigBox < Box

# добавить новый метод экземпляра

def printArea

@area = @width * @height

puts "Big box area is : #@area"

end

end


# создаёт объект

box = BigBox.new(10, 20)

# распечатать область

box.printArea()

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

Площадь большого ящика: 200

Переопределение методов (Methods Overriding):

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

#!/usr/bin/ruby ​​-w

# определить класс (define a class)

class Box

# метод конструктора (constructor method)

def initialize(w, h)

@width, @height = w, h

end

# экземпляр метода (instance method)

def getArea

@width * @height

end

end


# определить подкласс (define a subclass)

class BigBox < Box

# измените существующий метод getArea следующим образом

def getArea

@area = @width * @height

puts "Площадь большого ящика: #@area"

end

end


# создайте объект

box = BigBox.new(10, 20)

# распечатать область, используя метод переопределения.

box.getArea()

И в итоге:

Площадь большого ящика: 200

Перегрузка оператора (Operator Overloading):

Мы хотели бы, чтобы оператор + выполнял векторное сложение двух объектов 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

Замораживание объектов (Freezing objects):

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

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

#!/usr/bin/ruby ​​-w

# определение класса (define a class)

class Box

# метод конструктора (constructor method)

def initialize(w, h)

@width, @height = w, h

end

# методы доступа (accessor method)

def getWidth

@width

end

def getHeight

@height

end

# setter methods

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 object is frozen object"

else

puts "Box object is normal object"

end

# теперь попробуйте использовать методы установки

box.setWidth = 30

box.setHeight = 50

# используйте методы доступа

x = box.getWidth()

y = box.getHeight()

puts "Ширина блока : #{x}"

puts "Высота блока : #{y}"

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

Объект Box - это замороженный объект

test.rb: 20: в `setWidth = ': невозможно изменить замороженный объект (TypeError) из test.rb: 39

Константы класса (Class Constants):

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

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

#!/usr/bin/ruby ​​-w

# определение класса (define a class)

class Box

BOX_COMPANY = "TATA Inc"

BOXWEIGHT = 10

# метод конструктора (constructor method)

def initialize(w, h)

@width, @height = w, h

end

# экземпляр метода (instance method)

def getArea

@width * @height

end

end


# создать объект

box = Box.new(10, 20)

# вызвать методы экземпляра

a = box.getArea()

puts "Area of ​​the box is: #{a}"

puts Box::BOX_COMPANY

puts "Box weight is: #{Box::BOXWEIGHT}"

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

Area of the box is : 200

TATA Inc

Box weight: 10

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

Создайте объект с помощью allocate (Create object using allocate):

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

#!/usr/bin/ruby ​​-w

# определение класса (define a class)

class Box

attr_accessor :width, :height

# метод конструктора (constructor method)

def initialize(w, h)

@width, @height = w, h

end

# метод экземпляра (instance method)

def getArea

@width * @height

end

end

# создать объект с помощью new

box1 = Box.new(10, 20)

# создать другой объект с помощью allocate

box2 = Box.allocate

# вызвать метод экземпляра с помощью box1

a = box1.getArea()

puts "Area of ​​the box is: #{a}"

# вызов метода экземпляра с использованием box2

a = box2.getArea()

puts "Area of the box : #{a}"

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

Area of the box is : 200

test.rb: 14: предупреждение: переменная экземпляра @width не инициализирована

test.rb: 14: предупреждение: переменная экземпляра @height не инициализирована

test.rb: 14: в `getArea ': undefined method` * '

for nil : NilClass (NoMethodError) from test.rb: 29

Информация о классе (Class Information):

Если определения классов являются исполняемым кодом, это означает, что они выполняются в контексте некоторого объекта: self должен ссылаться на что-то.

#!/usr/bin/ruby -w

class Box

# печать информации о классе

puts "Type of self = #{self.type}"

puts "Name of self = #{self.name}"

end

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

Type of self = Class

Name of self = Box

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