В этой статье познкомимся с приципами объектно-ориентированнного программирования (ООП) и как они реализуются в JavaScript. Тем кто ранее знакомился с ООП в классических языках программитрования, как, например, Java, C++, то принципы могут показаться странными, так как в JavaScript есть объекты, но нет классов, по крайней мере, в их традиционном понимании.
Термины
ООП является парадигмой, это довольно распространенная парадигма, но далеко не единственная. Она разрабатывалась как попытка связать поведение сущности с её данными и спроеципровать объекты реального, и не очень, мира в программный код. Так же ООП возник как ответ на проблему возрастающих объемов программных кодов. Для того, чтобы описать объект необходимо ввести некоторые понятия: метод, свойство, класс, объект, экземпляр.
Стоить отметить, что в эволюции парадигм ООП предшевствовало процедурное программирование, в котором была попытка решить проблему дублирования кода. Таким образом в ООП основными элементами программы являются не переменные и методы (процедуры), а объекты.
Класс - шаблон для создания объектов. Класс обеспечивает начальные значения состояний: инициализация переменных и реализация поведений методов.
Объект - это программные конструкции, включающие набор логически связанных свойств и методов. Объекты создаются на основе шаблонов, которые называются классами и являются экземплярами этих классов. Обратите внимание, что термины "экземпляр класса" и "объект" взаимозаменяемы
Метод - это функция, с помощью которой можно оперировать данными класса.
Свойство - это характеристикиПолучаем следующую картину:
Класс = Свойства + Метод объекто класса.
Получаем следующую картину:
Класс = Свойства + Метод
В JavaScript нет классов!!! Но это не мешает использовать принципы ООП так как:
- в JavaScript достаточно просто создать объект;
- в JavaScript есть много встроенных объектов, которые не нужно создавать;
- можно создавать объекты на основе существующих объектов путем наследования. В этом случаОбъекте создаваемый объект повторяет структуру и содержание исходного объекта. Объект, на основе которого создается другой объект, называется родительским и служит прототипом для своего потомка (который называется дочерним объектом). Такой тип наследования называется прототипным.
Принципы ООП
Немного теории...
Принцип 1. Абстракция
Абстракция - принцип ООП, согласно которому объект характеризуется свойствами, которые отличают его от всех остальных объектов и при этом четко определяют его концептуальные границы.
Т. е. абстракция позволяет:
- Выделить главные и наиболее значимые свойства предмета.
- Отбросить второстепенные характеристики.
Когда мы имеем дело с составным объектом - мы прибегаем к абстракции. Например, мы должны понимать, что перед нами абстракция, если мы рассматриваем объект как "дом", а не совокупность кирпича, стекла и бетона. А если уже представить множество домов как "город", то мы снова приходим к абстракции, но уже на уровень выше.
Зачем нужна абстракция? Если мыслить масштабно - то она позволяет бороться со сложностью реального мира. Мы отбрасываем все лишнее, чтобы оно нам не мешало, и концентрируемся только на важных чертах объекта.
Абстракция представляет концепцию в более простой форме
Принцип 2. Инкапсуляция
Абстракция утверждает следующее: "Объект может быть рассмотрен с общей точки зрения". А инкапсуляция от себя добавляет: "И это единственная точка зрения, с которой вы вообще можете рассмотреть этот объект.". А если вы внимательно посмотрите на название, то увидите в нем слово "капсула". В этой самой "капсуле" спрятаны данные, которые мы хотим защитить от изменений извне.
Инкапсуляция - принцип ООП, согласно которому сложность реализации программного компонента должна быть спрятана за его интерфейсом.
На что обратить внимание?
- Отсутствует доступ к внутреннему устройству программного компонента.
- Взаимодействие компонента с внешним миром осуществляется посредством интерфейса, который включает публичные методы и поля.
А теперь опять пример с домом. Как в данном случае будет работать инкапсуляция? Она будет позволять нам смотреть на дом, но при этом не даст подойти слишком близко. Например, мы будем знать, что в доме есть дверь, что она коричневого цвета, что она открыта или закрыта. Но каким способом и из какого материала она сделана, инкапсуляция нам узнать не позволит.
Для чего нужна инкапсуляция?
- Инкапсуляция упрощает процесс разработки, т. к. позволяет нам не вникать в тонкости реализации того или иного объекта.
- Повышается надежность программ за счет того, что при внесении изменений в один из компонентов, остальные части программы остаются неизменными.
- Становится более легким обмен компонентами между программами.
Не дает взглянуть на внутреннюю реализацию сложной концепции. Что видите, то и получите - и не более того
Принцип 3. Наследование
Наследование - способ создания нового класса на основе уже существующего, при котором класс-потомок заимствует свойства и методы родительского класса и также добавляет собственные.
Снова перед нами объект Дом. Дом можно построить, отремонтировать, заселить или снести. В нем есть фундамент, крыша, окна и двери. В виде списка это может выглядеть следующим образом:
На что обратить внимание?
- Класс-потомок = Свойства и методы родителя + Собственные свойства и методы.
- Класс-потомок автоматически наследует от родительского класса все поля и методы.
- Класс-потомок может дополняться новыми свойствами.
- Класс-потомок может дополняться новыми методами, а также заменять(переопределять) унаследованные методы. Переопределить родительский метод - это как? Это значит, внутри класса потомка есть метод, который совпадает по названию с методом родительского класса, но функционал у него новый - соответствующий потребностям класса-потомка.
Для чего нужно наследование?
- Дает возможность использовать код повторно. Классы-потомки берут общий функционал у родительского класса.
- Способствует быстрой разработке нового ПО на основе уже существующих открытых классов.
- Наследование позволяет делать процесс написания кода более простым.
Наследование позволяет создать новый класс на основе уже существующего: берем свойства и методы класса-родителя + добавляем собственные свойства/методы
Принцип 4. Полиморфизм
Полиморфизм - это поддержка нескольких реализаций на основе общего интерфейса.
Другими словами, полиморфизм позволяет перегружать одноименные методы родительского класса в классах-потомках.
Также для понимания работы этого принципа важным является понятие абстрактного метода:
Абстрактный метод (он же виртуальный метод) - это метод класса, реализация для которого отсутствует.
А теперь попробуем собрать все воедино. Для этого снова обратимся к классам Дом, Частный дом и Многоквартирный дом. Предположим, что на этапе написания кода мы еще знаем, какой из домов(частный или многоэтажный) нам предстоит создать, но вот то, что какой-то из них придется строить, мы знаем наверняка. В такой ситуации поступают следующим образом:
- В родительском классе(в нашем случае - класс Дом) создают пустой метод(например, метод Построить() ) и делают его абстрактным.
- В классах-потомках создают одноименные методы, но уже с соответствующей реализацией. И это логично, ведь например, процесс постройки Частного и Многоквартирного дома отличается кардинально. К примеру, для строительства Многоквартирного дома необходимо задействовать башенный кран, а Частный дом можно построить и собственными силами. При этом данный процесс все равно остается процессом строительства.
- В итоге получаем метод с одним и тем же именем, который встречается во всех классах. В родительском - имеем только интерфейс, реализация отсутствует. В классах-потомках - имеем и интерфейс и реализацию. Причем в отличие от родительского класса реализация в потомках уже становится обязательной.
- Теперь мы можем увидеть полиморфизм во всей его красе. Даже не зная, с объектом какого из классов-потомков мы работаем, нам достаточно просто вызвать метод Построить(). А уже в момент исполнения программы, когда класс объекта станет известен, будет вызвана необходимая реализация метода Построить().
Как итог - за одинаковым названием могут скрываться методы с совершенно разным функционалом, который в каждом конкретном случае соответствует нуждам класса, к которому он относится.
полиморфизм позволяет перегружать одноименные методы родительского класса в классах-потмках, то есть название метода остается прежним, а его начинка изменяется под нужды конктетного класса.