Принципы объектно-ориентированного программирования
В данной статье рассматривается новый объектно-ориентированный язык программирования Crystal, анализируются его возможности и показывается, что он позволяет создавать современное эффективное программное обеспечение. Но сначала немного истории — вспомним основные принципы объектно-ориентированного программирования вообще.
Объектно-ориентированное программирование ( ООП ) — это парадигма программирования, основанная на концепции «объектов», которые могут содержать данные и код: данные в форме полей, часто называемые атрибутами или свойствами, и код в форме процедур, как правило, называемые методами. Любой современный объектно-ориентированный язык программирования позволяет создавать современное высокопроизводительное эффективное программное обеспечение.
Объектно-ориентированный язык программирования базируется на понятии «класс», который представляет собой пользовательский тип данных или шаблон для создания объектов – экземпляров класса. Тогда программа представляет собой совокупность взаимодействующих объектов.
Язык программирования называется объектно-ориентированным, если в нем реализованы следующие принципы:
- инкапсуляции;
- наследования;
- полиморфизма.
Напомним эти определения.
Инкапсуляция – это концепция объектно-ориентированного программирования, которая связывает воедино данные и функции(методы), которые манипулируют данными, и защищает их как от внешнего вмешательства, так и от неправильного использования(сокрытие данных и методов).
Наследование- это возможность создания новых классов (подклассов, производных классов) из существующих базовых классов, с последующим формированием их в иерархию классов.
Производный класс наследует все переменные экземпляра и все методы экземпляра и класса(суперкласса), включая его конструкторы (new и initialize). Применение иерархии классов делает управляемыми большие потоки информации.
Полиморфизм — это свойство(принцип), которое позволяет одно и то же имя использовать для решения двух или более схожих, но технически разных задач. Целью полиморфизма, применительно к объектно-ориентированному программированию, является использование одного имени для задания общих для класса действий. Выполнение каждого конкретного действия будет определяться типом данных.
Примечание. Объекты, созданные из классов, занимают память в «общей куче» и сборщику мусора приходится освобождать эту область памяти. Создание множества объектов может быстро истощить ресурсы ЭВМ. Программисту за этим следует следить постоянно.
Далее рассмотрим реализацию подробнее программную реализацию принципов ООП на примере нового языка программирования Crystal. Crystal — это высокоуровневый объектно-ориентированный язык программирования, в котором реализованы все принципы ООП.
Основы языка и его синтаксиса будут объясняться и сопровождаться примерами фрагментов программ, созданными в среде программирования языка Crystal.
Использование инкапсуляции в программах
Любой объектно-ориентированный язык программирования, в том числе и языка программирования Crystal, использует инкапсуляцию для сокрытия значений структурированных данных или состояния объекта внутри класса, предотвращая прямой доступ к ним со стороны клиентов таким образом, чтобы это могло раскрыть скрытые детали реализации или нарушить инвариантность состояния, поддерживаемую методами. Единицей инкапсуляции и наследования в ООП является класс.
Для реализации инкапсуляции в программах объектно-ориентированный язык программирования использует ограничение видимости (так называемая инкапсуляция кода). Управление доступом к переменным и методам класса осуществляется с помощью специальных ключевых слов public, protected или private, записываемых перед определением метода и означающих общедоступный, защищенный и закрытый, соответственно.
По умолчанию методы являются общедоступными, если нет ключевого слова public.
Ключевое слово protected перед методом класса означает, что метод может быть вызван только на экземплярах одного и того же типа, что и текущий тип, то есть в базовом и производных от него классах. Все примеры далее будут на языке программирования Crystal.
Например, объявим класс, имеющий два атрибута и два метода.
- class Sensor # имя класса
- @izmPar: Float32 # имя переменной класса вещественного типа
- def initialize(izmPar : Float32) # имя метода класса(инициализация объекта)
- @izmPar = izmPar # инициализация через параметр
- @speed = 0 инициализация присваиванием
- End
- def izmPar
- @izmPar
- end
- def speed
- @speed
- end
- end
Если в этом классе вторую строчку записать так private @izmPar: Float32, то получим ошибку:
Error: can’t apply visibility modifier
Ошибка: не удается применить модификатор видимости
Потому что модификатор видимости(доступа) применяется только к методам.
Если будет объявлен закрытый(частный) метод, например
private def izmPar
@izmPar
End
То использовать его в программе нельзя. Private м
етод может быть вызван только в том классе, где он объявлен. Если так,
sensor = Sensor.new(50,100)
sensor.izmPar,
то применение sensor.izmPar в программе приведет к ошибке: Error: private method ‘izmPar’ called for Sensor — Ошибка: для Sensor вызван частный метод «» izmPar
Аналогично и для защищенного(protected) метода.
Кроме того, объявление метода с явным указанием модификатора видимости приводит к ошибке.
public def izmPar
@izmPar
end
Error: can’t declare def dynamically.
Ошибка: не удается динамически объявить def.
Как же быть? Ответ прост – выполнять закрытый или защищенный методы можно внутри другого открытого метода класса.
Дополняем класс методом
def izmMer
puts sensor.izmPar
end
Теперь
sensor = Sensor.new(50,100)
sensor.izmMer,
метод sensor.izmPar выполнится без ошибки.

Грамотное применение уровней доступа к элементам класса хорошо обеспечивает безопасность кода программы на языке программирования Crystal и создавать эффективное программное обеспечение.
Перегрузка методов
Для расширения возможностей объектно-ориентированный язык программирования использует такое свойство методов как перегрузка. Это означает, что в классе могут быть разные методы с одинаковым именем и разным количеством и типом аргументов, с различной сигнатурой, и они будут рассматриваться как отдельные методы.
Перегрузка методов осуществляется по нескольким критериям:
Количество аргументов
Ограничения типа, применяемые к аргументам
Принимает ли метод блок или нет
Например, ниже определяется четыре разных метода с одним именем:
- class Proba
- @x: Int32
- def initialize(x : Int32)
- @x=x
- puts «Начальное значение», «#{@x}»
- end
- # Увеличение значения на единицу
- def increm
- @x += 1
- end
- # Увеличение значения на y
- def increm(y : Int32)
- @x += y
- end
- # Увеличение значения начисло, заданное строкой str
- def increm(str : String)
- @x += str.to_i
- end
- #Вырабатывает текущее значение и увеличивает его
- # на значение, возвращаемое блоком
- def increm
- @x += yield @x
- end
- end
- Создаем объект класса
- probn = Proba.new(3)
- Выполнякм методы с одним именем и различными параметрами.
- #probn.increm
- puts «Увеличенное значение на 1», probn.increm #=> 4
- #probn.increm(6)
- puts «Увеличенное значение на 6», probn.increm(6) #=> 10
- #probn.increm(«25»)
- puts «Увеличенное значение на 25», probn.increm(«25») #=> 36
- probn.increm do |current_x|
- current_x < 30 ? 15 : 40
- end
- puts «Увеличенное значение на число из блока», probn.increm #=> 76

Использование наследования в программах
Объектно-ориентированный язык программирования, и язык программирования Crystal в том числе, с целью уменьшения затрат на разработку программы применяет свойство наследования классов. Наследование позволяет программистам создавать классы, основанные на существующих классах, чтобы указать новую реализацию с сохранением того же поведения (реализация интерфейса), повторно использовать код и независимо расширять исходное программное обеспечение через общедоступные классы и интерфейсы.
В новом языке программирования Crystal предусмотрено одиночное наследование, обозначаемое: подкласс < суперкласс. Перемещение свойств и методов, объединенных в несколько классов, в один суперкласс позволяет всем им совместно использовать функциональные возможности. Таким образом, в подклассе можно использовать все переменные экземпляра и все методы суперкласса, включая конструкторы.
Каждый класс, кроме Object -
корня иерархии, наследуется от другого класса (его суперкласса).
Синтаксис объявления производного класса Temp от базового Sensor имеет вид.
class Temp < Sensor #объявлен пустой класс
end
Далее в программе запишем
sensor = Sensor.new(50,100)
temp=Temp.new(30,69) #создание объекта произвольного класса
sensor.izmPar #так нельзя:
Error: protected method ‘izmPar’ called for Sensor
Ошибка: для Sensor вызван защищенный метод izmPar
temp.izmPar #так можно, класс Temp наследует метод izmPar класса Sensor.
Класс Temp наследует все переменные и все методы суперкласса Sensor, включая его конструкторы ( new
и initialize
).
Методы в производном классе можно дополнять и переопределять. Но вызывать не переопределенные методы суперкласса нельзя. Например.
class Temp < Sensor
self.izmPar
end
Возникает ошибка на этапе компиляции.
Error: undefined method ‘izmPar’ for Temp.class
Ошибка: неопределенный метод “izmPar” для Temp.class

Можно также переопределить любой унаследованный метод в подклассе. Если подкласс определяет собственные методы initialize для инициализации, то они не наследуются. Если вы хотите использовать функциональность суперкласса после его переопределения, можно вызвать любой метод суперкласса с помощью «super»
Использование полиморфизма в программах
В более общем смысле, концепцией полиморфизма является идея «один интерфейс, множество методов». Это означает, что можно создать общий интерфейс(сигнатуру) для группы близких по смыслу действий. Преимуществом полиморфизма является то, что он помогает снижать сложность программ, разрешая использование того же интерфейса для задания единого класса действий. Выбор же конкретного действия, в зависимости от ситуации, возлагается на компилятор. Программисту, не нужно делать этот выбор самому. Нужно только помнить и использовать общий интерфейс.
По сути полиморфизм обеспечивает разное поведение одного и того же метода в разных классах, при этом действия, совершенные с объектами могут существенно различаться. Например.
- Class sumInt
- n=10
- def summa(self,n)
- self.summa= self.n + n
- end
- end
- Class sumString
- n=10
- def summa(self,s)
- self.summa= len(snr(s)
- end
- end
- si = sumInt()
- ss = sumString()
- si.summa(45)
- нss.summa(45)
- print(si.summa) # результат 55
- print(ss.summa) # результат 2
В данном примере метод с одним и тем же именем summa() используется как для суммирования целых чисел, так и для определения количества цифр в числе(длины строки).
Таким образом, объектно-ориентированный язык программирования Crystal имеет все возможности создавать современное эффективное программное обеспечение и позволяет решать широкий круг вычислительных задач.
Пример использования ООП для решения задачи
Для примера использования ООП в языке программировании Crystal рассмотрим задачу измерения температуры и давления воздуха. Для получения измерений применяется два датчика – один в качестве датчика температуры, другой — датчика давления.
В программе предусматривается базовый класс Sensor, отражающий общие свойства любого датчика, и два производных от него класса SensorTemp и SensorDavl, соответственно, представляющие объекты — датчик температуры и давления. Измерения передаются в эти классы для хранения.
В качестве измеренного значения формируется среднее арифметическое всех измерений — сумма всех измерений, деленная на их количество. Для этого создается отдельный класс Izmer, в котором в методе srZnach осуществляется суммирование элементов массива измерений и вычисление среднего значения.
Код программы.
- class Sensor #измеритель — базовый класс
- @n=5
- @k=15
- @sum=0
- @izmPar =[] of Float64 #создаем пустой массив Array(Float64)
- def initialize(@n : Int32, @k : Int32, izmPar=[] of Float64)
- z = gen(n,k,izmPar) # генерируем массив izmPar(Float64) из n элементов
- # puts izmPar # оператор для отладки программы
- end
- def gen(n,k,mas=[] of Float64)
- i=0
- while i<n
- mas<<k+0.01*k*rand
- i+=1
- end
- # puts mas # оператор для отладки программы
- end
- end
- class SensorTemp < Sensor #измеритель температуры — производный класс
- def srTemp # метод вычисления среднего значения температуры
- end
- end
- class SensorDavl < Sensor #измеритель давления — производный класс
- def srDavl # метод вычисления среднего значения давления
- end
- end
- class Izmer # вычислитель самостоятельный класс
- def initialize()
- end
- def srZnach(n,mas=[] of Float64)
- i=0
- sum=0
- while i<n
- sum += mas[i]
- i+=1
- end
- srZnach=sum/n
- # puts «Среднее в классе», srZnach # оператор для отладки программы
- return srZnach
- end
- end
# Программа вычисления температуры и давления воздуха.
- puts «Имитация измерителя температуры и давления»
- n=5
- srGenT=25
- srGenD=760
- izmPar =[] of Float64 #создаем пустой массив Array(Float64)
- sensor = Sensor.new(n,5,izmPar)
- izmer=Izmer.new()
- izmTemp =[] of Float64
- sensTemp = SensorTemp.new(n,srGenT,izmTemp)
- printf «Массив температур — «
- #puts «\n», izmTemp # оператор для отладки программы
- izmTemp.each_with_index{ |n, idx| print «%#{3.2}f » % n; print «\n» if idx % 10 == 5 }
- srTemp=izmer.srZnach(n,izmTemp)
- puts «\n», «Средняя температура», «%3.2f» %srTemp
- izmDavl =[] of Float64
- sensDavl = SensorDavl.new(n,srGenD,izmDavl)
- printf «Массив давлений — «
- izmDavl.each_with_index{ |n, idx| print «%#{3.2}f » % n; print «\n» if idx % 10 == 5 }
- srDavl=izmer.srZnach(n,izmDavl)
- puts «\n», «Среднее давление», «%3.2f» %srDavl
Результаты выполнения приведенной выше программы в среде разработки и выполнения языка Crystal представлены на следующем рисунке.

Анализ представленных результатов свидетельствует о работоспособности программы на языке Crystal. Это подтверждает возможности языка программирования Crystal создавать достаточно сложное и эффективное программное обеспечение.
Заключение
Crystal — это высокоуровневый объектно-ориентированный язык программирования. Объектно-ориентированный язык программирования Crystal позволяет создавать современное эффективное программное обеспечение.
Инкапсуляция используется в программах для сокрытия значений или состояния объекта структурированных данных внутри класса.
В языке Crystal предусмотрено одиночное наследование классов.
Другой вариант объектно-ориентированного программирования при создании оконного приложения методом событийно-визуального программирования представлен в статье.
Статьи по теме
- Что такое программирование?
- Как выбирать язык программирования высокого уровня для изучения
- Что выбирать язык или платформу программирования?
- Онлайн школы и курсы IT профессий
- Как стать программистом?
- Как изучать алгоритмы решения задач?
- Как изучать простые типы данных языка программирования?
- Как стать специалистом по большим данным?