В мире программирования можно услышать очень много странных и непонятных сперва аббревиатур. Одна из таких - ООП - скрывает за собой целое поколение программ и программистов, проблем и их решений, а главное - ленивых людей.
Видеоверсия | Презентация |
История происхождения
Программы делали достаточно давно. Сначала их делали учёные для произведения сложных и нудных вычислений, которые необходимы были для их исследований. Потом начали появляться программы посложнее и поинтереснее - о компьютерах заговорили не только как о калькуляторах, но и как о машинах, способных делать много разных интересных вещей и сразу.
Через некоторое время компьютеры и программы вышли на очень важный этап развития любой технологии - военное дело. Потом пошла коммерция и рынок, а там уже недалеко и игры :D
Структуры
Программы становились всё сложнее и сложнее. Людям перестало хватать стандартных типов данных - чисел (много типов чисел), массивов и булевых значений. Данных становилось всё больше, но смысл не менялся, их нужно было как-то обрабатывать, складывать, умножать, делить, искать среднее, квадраты, логорифмы и остальную статистику/математику. Для хранения этих данных создавались переменные, но место для хранения этих переменных запрашивалось у операционной системы в разное время и значения раскидывались по памяти как попало.
[a]
[$] [c] [d]
[S] [r]
[T] [f]
[v]
Скорость доступа к таким переменным была маленькая…
Программистам требовалось описывать какое-то состояние чего-либо, например ракеты в какой-нибудь игре, с помощью большого количества переменных - направление киля, скорость и направление ветра, масса, мощьность головки и т. д. Но ракет на складе много, значит нужен массив. Для вычисления направления киля 15 ракет для достижения всеми ими определённой точки нужно, допустим, 3 параметра - скорость ветра, точка запуска, скорость движения ракеты. Для каждой ракеты делать переменные Vwind_1
, Vwind_2
… Vwind_15
никто не хотел, поэтому они хранились в массиве, где индекс был номером ракеты: Vwind_i = [10, 200, 35, 47, ...]
. Такой массив был относительно быстрее и мог хранить много значений в одной переменной.
[v:12, 34, 56] [s:2, 346, 633, 87]
[r:45, 6916, 7, 473, 3258, ... ]
Скорость досутпа была чуть выше, но всё равно все алгоритмы страдали от того, что данные одной ракеты разбросаны не только по памяти, но и по разным переменным, название каждой нужно было знать и помнить её смысл. А если одна из ракет взрывалась? А если игрок поставил турель и она постоянно пускает ракеты - это же ужас! Нужно следить чтобы во всех массивах не просто было одинаковое количество элементов, но и каждый элемент соответствовал элементам в других массивах, чтобы не получалось ракет-мутантов, которые только что вылетили из ствола, а уже имеют скорость света.
Решением стали структуры - новый тип данных, который состоял из нескольких переменных (свойств), хранящихся в памяти друг рядом с другом. Доступ к значениям этих переменных осуществлялся так же как к элементам массива.
Получалось что-то такое:
[Vel_x,Vel_y,Mass,X,Y]
Такой тип данных прекрасно хранится в массиве, так как каждое свойство имеет свой размер, значит можно узнать и размер всей структуры.
[[Vel_x,Vel_y,Mass,X,Y], [Vel_x,Vel_y,Mass,X,Y], ...]
А самое главное - теперь всё хранится в одном месте - не нужно думать о нескольких массивах из которых надо удалять элементы.
Так объекты обзавелись свойствами.
Не повторяй свой код!
Теперь, когда есть структуры и данные хранить просто и легко, можно спокойно программировать дальше. Делать ракетницы, солдат, танки и остальную игровую технику, необходимую для расставления на тактическом поле. Теперь единицы техники и личного состава описываются структурами, а девизии и полки для программистов были массивами.
Но полки нужно как-то передвигать на поле. Для этого для каждого типа техники и солдата были написаны алгоритмы, передвигающие объект на поле. Получилось много функций, делающих по сути одно и тоже - изменяющих координаты объектов на поле. Тогда было решено соединить их все в одну функцию move
, но поскольку объектов много и все разные, внутри этой функции стало слишком много конструкций if ... else
, просто определяющих скорость машины или человека. Логично не определять скорость в функции каждый раз, а хранить её где-то внутри самого объекта, например в поле max_speed
.
Так появилось два очень важное понятие в ООП - базовые свойства. Функция move
могла принимать на вход любой объект, который имел 3 базовых для этой функции свойства: X, Y и максимальную скорость.
Через какое-то время стало понятно, что на самом деле не нужно каждый раз записывать в объект одно и тоже число - максимальную скорость - и это число стали хранить отдельным свойством (static) в одной переменной, но доступ к которой был таким же, как и к любому другому свойству объекта. Таким образом сохранили немного памяти.
“А давайте вообще всё хранить в одном месте”
До этого программисты уже поняли, что хранить функцию move
в глобальной области видимости - не хорошо, но и в модуле её хранить как-то не очень приятно.
Теперь у наших ленивых героев в голове появилась гениальная идея: “А давайте будем хранить функцию move
внутри структур как static-свойство!”
Эти static-свойства, являющиеся функциями, теперь называются методами. А структура с методами называется классом.
Дурная наследственность…
Ещё через какое-то время программистам дали задачу - реализовать танкам повороты, то есть перед тем, как ехать к какой-то точке им нужно было направиться к этой точке передом.
Ещё одна интереснейшая мысль спустилась на головы людей: “Давайте даже сделаем лучше, сделаем один класс Movable
, который будет иметь X
, Y
, V
и move
и заставим остальные классы так же иметь эти свойства! А для Tank
просто сделаем свою move
, которая будет сначала поварачиваться, а потом уже и двигаться”.
Таким образом появились понятия предок - класс Movable
- и наследник. Смысл был в том, чтобы сделать один функционал для всех и потом наследовать его, то есть использовать один и тот же, не копируя ни строчки кода. А для тех, кому нужны какие-то изменения - создавать новый метод или свойство.
Потом оказалось, что не всегда можно сразу всем задать один и тот же способ передвижения, но код был заточен на то, что все классы являются наследниками Movable
. Программисты стали писать, что все классы, с которыми работает этот код должны бы быть наследниками Movable
. Такой класс, к которому “должны бы наследоваться другие” назвали интерфейсом. А саму формулировку “должно бы наследоваться” заменили на “должно реализовывать интерфейс”.