Что такое объектно-ориентированное программирование

Циклы, ветвления и функции – все это элементы структурного программирования. Его возможностей вполне хватает для написания небольших, простых программ и сценариев. Однако крупные проекты часто реализуют, используя парадигму объектно-ориентированного программирования (ООП). Что оно из себя представляет и какие преимущества дает?

Истоки ООП берут начало с 60-х годов XX века. Однако окончательное формирование основополагающих принципов и популяризацию идеи следует отнести к 80-м годам. Большой вклад внес Алан Кей.

Следует отметить, что хоть и многие, но не все современные языки поддерживают объектно-ориентированное программирование. Так язык C, обычно используемый в системном программировании (создание операционных систем, драйверов, утилит), не поддерживает ООП.

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

Итак, что же такое объектно-ориентированное программирование? Судя по названию, ключевую роль здесь играют некие объекты, на которые ориентируется весь процесс разработки.

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

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

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

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

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

Основными понятиями, используемыми в ООП, являются класс, объект, наследование, инкапсуляция и полиморфизм. В языке Python класс равносилен понятию тип данных.

Класс и объект

Что такое класс или тип? Проведем аналогию с реальным миром. Если мы возьмем конкретный стол, то это объект, но не класс. А вот общее представление о столах, их назначении – это класс. Ему принадлежат все реальные объекты столов, какими бы они ни были. Класс столов дает общую характеристику всем столам в мире, он их обобщает.

То же самое с целыми числами в Python. Тип int – это класс целых чисел. Числа 5, 100134, -10 и т. д. – это конкретные объекты этого класса.

В языке программирования Python объекты принято называть также экземплярами. Это связано с тем, что в нем все классы сами являются объектами класса type. Точно также как все модули являются объектами класса module.

>>> a = 145
>>> b = ['one', 'two']
>>> type(a)
<class 'int'>
>>> type(b)
<class 'list'>
>>> 
>>> type(int), type(list)
(<class 'type'>, <class 'type'>)
>>> 
>>> import math
>>> type(math)
<class 'module'>

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

Наследование, инкапсуляция, полиморфизм

Следующее по важности понятие объектно-ориентированного программирования – наследование. Вернемся к столам. Пусть есть класс столов, описывающий общие свойства всех столов. Однако можно разделить все столы на письменные, обеденные и журнальные и для каждой группы создать свой класс, который будет наследником общего класса, но также вносить ряд своих особенностей. Таким образом, общий класс будет родительским, а классы групп – дочерними, производными.

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

Основное (но не единственное) преимущество, которое дает концепция наследования в программировании, – это вынос одинакового кода из разных классов в один родительский класс. Другими словами, наследование позволяет сводить на нет повторение кода в разных частях программы.

Инкапсуляция в ООП понимается двояко. Во многих языках этот термин обозначает сокрытие данных, то есть невозможность напрямую получить доступ к внутренней структуре объекта, так как это небезопасно. Например, наполнить желудок едой можно напрямую, положив еду в желудок. Но это опасно. Поэтому прямой доступ к желудку закрыт. Чтобы наполнить его едой, надо совершить ритуал, через элемент интерфейса под названием рот.

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

Отсутствие сокрытия данных в Python делает программирование на нем проще, но привносит ряд особенностей, связанных с пространствами имен.

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

Полиморфизм можно перевести как множество форм. В ООП под полиморфизмом понимается следующее. Объекты разных классов, с разной внутренней реализацией, то есть программным кодом, могут иметь "одинаковые" методы. На самом деле у методов совпадают только имена, а вложенный в них код (то, что они делают) различен. Вот и получается, что у одного имени как бы множество форм.

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

Вы уже сталкивались с полиморфизмом операции +. Для чисел она обозначает сложение, а для строк – конкатенацию. Внутренняя реализация кода для этой операции у чисел отличается от реализации таковой для строк.

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

Пример объектно-ориентированной программы на Python

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

В программе определены три класса. Класс A является родительским по отношению к B и C. Последние наследуют от A поле-переменную field1 и метод make_str, который потом переопределяют.

От каждого класса мы создаем по одному объекту и присваиваем их переменным a, b, c. Метод make_str выводит на экран значения полей объекта. У объектов разных классов разный набор полей, поэтому код метода отличается.

class A:
    field1 = 1
 
    def make_str(self):
        print(self.field1)
 
 
class B(A):
    field2 = 2
 
    def make_str(self):
        print(self.field1, self.field2)
 
 
class C(A):
    field3 = 3
 
    def make_str(self):
        print(self.field1, self.field3)
 
 
a = A()
b = B()
c = C()
 
for i in (a, b, c):
    i.make_str()

Результат выполнения программы:

1
1 2
1 3

Практическая работа

Рассмотрите схему. Подумайте над следующими вопросами:

  1. Какие фигуры на ней вы бы назвали классами, а какие – объектами? Что обозначают пунктирные линии?

  2. Может ли объект принадлежать множеству классов? Может ли у класса быть множество объектов?

  3. Звезды скорее обладают разными свойствами или разным поведением? Могут ли свойства оказывать влияние на поведение?

  4. Что могли бы обозначать стрелки?

Курс с примерами решений практических работ:
pdf-версия


Объектно-ориентированное программирование на Python




Все разделы сайта