Объектно-ориентированное программирование (ООП) — это такой подход к проектированию систем, при котором программа представляется как совокупность объектов, взаимодействующих друг с другом.
Функции являются базовыми строительными блоками. Объекты — следующий шаг. Они группируют уже не строки кода как функции, а сами функции и данные, с которыми они работают.
Но при этом, объект — это не просто группировка функций.
Чтобы ООП-подход «работал», объект должен представлять собой законченную, интуитивно понятную сущность. Например, «пользователь», «меню», «элемент»…
Посмотрим на примере, чем может быть полезно ООП.
Меню, процедурный код
Например, пусть у нас есть HTML-элемент, представляющий собой «меню», вот такой:
<div id="food-menu" class="menu">
<span class="menu-title">Продуктовое меню</span>
<ul class="menu-items">
<li>Сыр</li>
<li>Колбаса</li>
<li>Баранки</li>
</ul>
</div>
Меню семантически размечено. Класс menu-title обозначает заголовок, а menu-items — список пунктов.
Как управлять этим меню из JavaScript? Для того, чтобы открывать-закрывать его, можно создать функции open и close:
function open(menuId) {
*!*var itemsElem*/!* = document.querySelector('#'+menuId+' .menu-items');
itemsElem.style.display = 'block';
}
function close(menuId) {
*!*var itemsElem*/!* = document.querySelector('#'+menuId+' .menu-items');
itemElem.style.display = 'none';
}
Сразу же бросается в глаза дублирование кода. В обоих функциях первая строка ищет itemsElem. Если, например, мы захотим изменить логику поиска или даже переименовать класс menu-items — придется делать это в двух местах. Неудобно, и можно просто забыть где-то поправить.
В данном случае дублирования можно избежать, если вынести этот код в третью функцию getItemsElem(menuId):
function getItemsElem(menuId, yesno) {
return document.querySelector('#'+menuId+' .menu-items');
}
function open(menuId) {
var itemsElem = getItemsElem(menuId);
itemsElem.style.display = 'block';
}
function close(menuId) {
var itemsElem = getItemsElem(menuId);
itemsElem.style.display = 'none';
}
То, что мы сейчас делаем, называется процедурный подход. Задача решается путем создания функций, одна функция вызывает другие.
Допустим, нам нужно добавить еще третью функцию getOpen(menuId), которая возвращает текущее состояние меню, четвертую — addItem(menuId, title), и которая добавляет элемент в меню и так далее..
Неужели они должны каждый раз вызывать getItemsElem? Ведь элемент не меняется, зачем лишние расходы на вызовы?
Лучше один раз получить элемент в переменную itemsElem и запомнить в таком месте, к которому потом легко обращаться. Но в каком?
Объект «меню» будет удобным хранилищем свойств этой сущности.
Кроме того, посмотрим на названия функций. Они очень общие, особенно open и close. В проекте уже могут быть другие функции с такими именами.
Можно добавить префикс menu: menuOpen, menuClose, menuGetListElem… Но лучще — записать функции в объект «меню», тогда префикс не понадобится и название функции останется коротким.
Меню, объектный код
Итак, мы видим, что объекты могут быть полезны, и для функций и для данных.
Пойдём дальше и создадим объект foodMenu для управления нашим меню:
var foodMenu = {
itemsElem: document.querySelector('#food-menu .menu-items'),
open: function() {
this.itemsElem.style.display = 'block';
},
close: function() {
this.itemsElem.style.display = 'none';
}
}
// использование
foodMenu.open();
Конструкторы
В примере выше объект управляет ровно одним конкретным меню foodMenu. Это правильно: одна сущность — один объект.
А что, если нам нужно сделать несколько меню на странице?
Для того, чтобы объявлять много похожих объектов, придуманы функции-конструкторы. В такой функции описывается, как создавать объект, его свойства и методы. А дальше можно создать хоть тысячу меню.
Функция-конструктор для меню будет такой:
function Menu(menuId) {
this.itemsElem = document.querySelector('#'+menuId+' .menu-items');
this.open = function() {
this.itemsElem.style.display = 'block';
};
this.close = function() {
this.itemsElem.style.display = 'none';
};
}
Использование:
var foodMenu = new Menu('food-menu');
foodMenu.open();
Результатом выполнения кода new Menu('food-menu') будет в точности такой же объект foodMenu, как и в предыдущем примере.
Итого: процедурный/объектный подходы
Процедурный код основан на функциях. Одна функция вызывает другую, и за счет этого реализуются сложные алгоритмы.
Объектно-ориентированное программирование — это когда сущности определяются при помощи объектов. В процессе выполнения эти сущности создаются, уничтожаются, общаются друг с другом (вызывают методы).
Это — сама основа ООП, все остальное вторично.
Применимость ООП
Как и любой образ мыслей, объектно-ориентированный подход может быть удобен или не удобен, в зависимости от ситуации.
Бывают задачи, в которых сложно выделить сущность. Например, «алгоритм нахождения наименьшего общего делителя». В нём есть два числа и численные операции над ними. Для него замечательно подойдёт процедурный подход.
В эту же группу попадает большинство математических алгоритмов, в которых есть сложные вычисления без выделения дополнительных сущностей. Именно поэтому древние языки программирования, предназначенные для расчетов (Фортран, Алгол) не содержали объектных возможностей.
С другой стороны, при разработке интерфейсов мы имеем дело с ярко выраженными сущностями.
Посетитель открывает окно браузера, затем нажимает кнопку, при этом раскрывается меню. Выбор пункта меню приводит к запросу на сервер — все выделенные слова здесь являются сущностями.
Поэтому ООП отлично подходит для веб-разработки. Какие-то сущности можно оформлять в объект, а какие-то — нет, это уже решает разработчик.
Мы продолжим разбор этого подхода в следующих главах.
Комментарии
- Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
- Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
- Комментарии без смысла, с рекламой или не о статье вообще - удаляются.