
Содержание статьи
от Нишанта Мишры

Будучи студентом информатики, я провожу много времени, изучая новые языки и играя с ними. Каждый новый язык может предложить нечто уникальное. Ввиду этого большинство начинающих начинают свой путь программирования с процедурных языков, таких как C, или с объектно-ориентированных языков, таких как JavaScript и C++.
Поэтому имеет смысл ознакомиться с основами объектно-ориентированного программирования, чтобы вы могли понять концепции и применить их к легко изучаемым языкам. Мы будем использовать язык программирования Ruby в качестве примера.
Вы можете спросить почему Ruby? Потому что он создан, чтобы сделать программистов счастливыми, а также потому, что почти все в Ruby являются объектами.
Знакомство с объектно-ориентированной парадигмой (ООП)
В ООП мы определяем вещи, которые обрабатывает наша программа. Как люди, мы думаем о вещах как объектах с атрибутами и поведением, и мы взаимодействуем с вещами на основе этих атрибутов и поведения. Вещью могут быть машина, книга и т.д. Такие вещи становятся классами (чертежами объектов), и мы создаем объекты из этих классов.
Каждый экземпляр (объект) содержит переменные экземпляра, являющиеся состоянием объекта (атрибуты). Поведение объектов представлено методами.
Возьмем для примера автомобиль. Автомобиль – это то, что сделало бы его класс. Конкретный тип автомобиля, скажем, BMW есть объект класса Автомобиль. The атрибуты/свойства BMW, например цвет и номер модели, можно хранить в сменных экземплярах. И если вы хотите выполнить операцию с объектом, например вождение, тогда «привод» описывает поведение, определяемое как метод.
Краткий урок синтаксиса
- Чтобы закончить строку в программе Ruby, точка с запятой (;) необязательна (но обычно не используется)
- Рекомендуется 2-пробеловое отступление для каждого вложенного уровня (не обязательно, как у Python)
- Без фигурных скобок
{}
используются, и конец Ключевое слово используется для обозначения конца блока управления потоком - Для комментариев мы используем
#
символ
Объекты создаются в Ruby путём вызова a новый в классе, как в примере ниже:
class Car def initialize(name, color) @name = name @color = color end
def get_info "Name: #{@name}, and Color: #{@color}" endend
my_car = Car.new("Fiat", "Red")puts my_car.get_info
Чтобы понять, что происходит в коде выше:
- У нас есть класс под названием
Car
двумя способами,initialize
иget_info
. - Сменные экземпляра в Ruby начинаются с
@
(Например@name
). Интересная часть состоит в том, что переменные поначалу не объявляются. Они возникают при первом использовании, а затем доступны для всех методов экземпляров класса. - Вызов
new
метод вызываетinitialize
метод вызова.initialize
это специальный метод, используемый как конструктор.
Доступ к данным
Сменные экземпляры являются частными, и к ним невозможно получить доступ вне класса. Чтобы получить доступ к ним, нам нужно создать методы. Методы экземпляров имеют публичный доступ по умолчанию. Мы можем ограничить доступ к этим методам экземпляров, как мы увидим в этой статье.
Чтобы получить и изменить данные, нам нужны методы getter и setter соответственно. Давайте рассмотрим эти способы на том же примере автомобиля.
class Car def initialize(name, color) # "Constructor" @name = name @color = color end
def color @color end
def color= (new_color) @color = new_color endend
my_car = Car.new("Fiat", "Red")puts my_car.color # Red
my_car.color = "White"puts my_car.color # White
В Ruby «getter» и «setter» определены с тем же именем, что и сменная экземпляра, с которой мы имеем дело.
В примере выше, когда мы говорим my_car.color
это действительно вызывает color
метод, который в свою очередь возвращает название цвета.
Примечание. Обратите внимание на то, как Ruby допускает пробел между color
и равно знаку при использовании сеттера, даже если название метода color=
Написание этих методов getter/setter позволяет нам иметь больше контроля. Но чаще всего получить имеющееся значение и установить новое значение просто. Следовательно, должен быть более простой способ вместо фактического определения методов получения/настройки.
Более простой способ
Используя attr_*
вместо этого мы можем получить существующее значение и установить новое значение.
attr_accessor
: как для получения, так и для установкиattr_reader
: только для получателяattr_writer
: только для сеттера
Давайте рассмотрим эту форму на том же примере автомобиля.
class Car attr_accessor :name, :colorend
car1 = Car.newputs car1.name # => nil
car1.name = "Suzuki"car1.color = "Gray"puts car1.color # => Gray
car1.name = "Fiat"puts car1.name # => Fiat
Таким образом, мы можем вообще пропустить определение геттера/сеттера.
Разговор о лучших практиках
В примере выше мы не инициализировали значение для @name
и @color
переменные экземпляра, не являющиеся хорошей практикой. Кроме того, поскольку переменные экземпляра установлены на ноль, объект car1
не имеет никакого смысла. Всегда полезно устанавливать переменные экземпляра с помощью конструктора, как в примере ниже.
class Car attr_accessor :name, :color def initialize(name, color) @name = name @color = color endend
car1 = Car.new("Suzuki", "Gray")puts car1.color # => Gray
car1.name = "Fiat"puts car1.name # => Fiat
Методы класса и переменные класса
Следовательно, способы класса вызываются в классе, а не в экземпляре класса. Это похожи на статический методы в Java
Примечание: self
вне определения метода ссылается на объект класса. Переменные класса начинаются с @@
На самом деле существует три способа определения методов класса в Ruby:
Внутри определения класса
- Использование ключевого слова self с названием метода:
class MathFunctions def self.two_times(num) num * 2 endend
# No instance createdputs MathFunctions.two_times(10) # => 20
2. Использование <<
; себя
class MathFunctions class << self def two_times(num) num * 2 end endend
# No instance createdputs MathFunctions.two_times(10) # => 20
Вне определения класса
3. Использование имени класса с названием метода
class MathFunctionsend
def MathFunctions.two_times(num) num * 2end
# No instance createdputs MathFunctions.two_times(10) # => 20
Наследование классов
В Ruby каждый класс неявно наследует класс Object. Давайте рассмотрим пример.
class Car def to_s "Car" end
def speed "Top speed 100" endend
class SuperCar < Car def speed # Override "Top speed 200" endend
car = Car.newfast_car = SuperCar.new
puts "#{car}1 #{car.speed}" # => Car1 Top speed 100puts "#{fast_car}2 #{fast_car.speed}" # => Car2 Top speed 200
В приведенном выше примере SuperCar
класс заменяет speed
метод, унаследованный от Car
класс. Символ &
lt; обозначает наследственность.
Примечание: Ruby не поддерживает множественное наследование, поэтому вместо него используются дополнения. Мы обсудим их в этой статье.
Модули в Ruby
Модуль Ruby является важной частью языка Ruby. Это основная объектно-ориентированная функция речи, которая косвенно поддерживает множественное наследование.
Модуль – это контейнер для классов, методов, констант или даже других модулей. Как и класс, модуль не может быть создан, но служит двум основным целям:
Модули как пространство имен
Многие языки, такие как Java, имеют идею структуры пакетов, чтобы избежать столкновения между двумя классами. Давайте рассмотрим пример, чтобы понять, как это работает.
module Patterns class Match attr_accessor :matched endend
module Sports class Match attr_accessor :score endend
match1 = Patterns::Match.newmatch1.matched = "true"
match2 = Sports::Match.newmatch2.score = 210
В приведенном выше примере мы имеем два класса с именами Match
мы можем различать их и предотвращать столкновения, просто инкапсулируя их в разные модули.
Модули как Mix-in
В объектно-ориентированной парадигме у нас есть концепция интерфейсов. Mix-in обеспечивает способ обмена кодом между несколькими классами. Кроме того, мы можем также включить такие встроенные модули, как Enumerable
и значительно облегчает нашу задачу. Давайте рассмотрим пример.
module PrintName attr_accessor :name def print_it puts "Name: #{@name}" endend
class Person include PrintNameend
class Organization include PrintNameend
person = Person.newperson.name = "Nishant"puts person.print_it # => Name: Nishant
organization = Organization.neworganization.name = "freeCodeCamp"puts organization.print_it # => Name: freeCodeCamp
Смешивание чрезвычайно мощно, поскольку мы пишем код только один раз, а затем можем включить их в любом случае при необходимости.
Сфера действия в Ruby
Мы увидим, как область работает для:
Область переменных
Методы и классы определяют новую область для переменных, а внешние переменные области не переносятся во внутреннюю область. Давайте посмотрим, что это значит.
name = "Nishant"
class MyClass def my_fun name = "John" puts name # => John end
puts name # => Nishant
Наружный name
переменная и внутрь name
переменные не одинаковы. Наружный name
переменная не переносится во внутреннюю область. Это означает, что если вы попытаетесь напечатать его во внутренней области без повторного определения, возникнет исключение. такой переменной не существует
Область применения констант
Внутренняя область может видеть константы, определенные в наружной области, а также может заменять внешние константы. Но важно помнить, что даже после замены постоянного значения во внутренней области значение во внешней области остается неизменным. Давайте посмотрим на это в действии.
module MyModule PI = 3.14 class MyClass def value_of_pi puts PI # => 3.14 PI = "3.144444" puts PI # => 3.144444 end end puts PI # => 3.14end
Область применения блоков
Блоки наследуют внешнюю область. Давайте поймем это на фантастическом примере, который я нашел в Интернете.
class BankAccount attr_accessor :id, :amount def initialize(id, amount) @id = id @amount = amount endend
acct1 = BankAccount.new(213, 300)acct2 = BankAccount.new(22, 100)acct3 = BankAccount.new(222, 500)
accts = [acct1, acct2, acct3]
total_sum = 0accts.each do |eachAcct| total_sum = total_sum + eachAcct.amountend
puts total_sum # => 900
В приведенном выше примере, если мы используем метод для вычисления total_sum
, total_sum
variable будет совсем другой переменной внутри метода. Вот почему иногда использование блоков может сэкономить много времени.
Ввиду этого переменная, созданная внутри блока, доступна только для блока.
Управление доступом
Разрабатывая класс, важно подумать о том, какую часть вы откроете миру. Это известно как инкапсуляция, и обычно означает сокрытие внутреннего представления объекта.
У Ruby существует три уровня контроля доступа:
- Общественный — контроль доступа не производится. Кто угодно может назвать эти методы.
- Защищенный могут быть вызваны объектами определяющих классов или их подклассов.
- Частный — не может быть вызван, кроме как посредством явного получателя.
Давайте посмотрим пример инкапсуляции в действии:
class Car def initialize(speed, fuel_eco) @rating = speed * comfort end
def rating @rating endend
puts Car.new(100, 5).rating # => 500
Теперь, поскольку подробности того, как вычисляется рейтинг, сохраняются внутри класса, мы можем изменить его в любой момент времени без каких-либо других изменений. Кроме того, мы не можем установить рейтинг извне.
Если говорить о способах определения контроля доступа, то их два:
- Если указать общедоступный, защищенный или приватный, и все, пока следующее ключевое слово управления доступом будет иметь этот уровень контроля доступа.
- Регулярно определяйте метод, затем указывайте общедоступный, частный и защищенный уровни доступа и перечисляйте методы, разделенные запятыми (,), под этими уровнями с помощью символов методов.
Пример первого способа:
class MyClass private def func1 "private" end protected def func2 "protected" end public def func3 "Public" endend
Пример второго способа:
class MyClass def func1 "private" end def func2 "protected" end def func3 "Public" end private :func1 protected :func2 public :func3end
Примечание: часто используются публичные и частные средства управления доступом.
Вывод
Это самые основы объектно-ориентированного программирования в Ruby. Теперь, зная эти концепции, вы можете глубже пойти и изучить их, создавая крутые вещи.
Не забудьте аплодировать и следовать, если вам понравилось! Не отставай от меня здесь.