Это будет текст об архитектуре объектно-ориентированных программ. Это для тех, кто планирует серьёзно заниматься разработкой.
В объектно-ориентированном программировании многое основано на объектах — специальных «коробках» из данных и функций, которые работают в программе как единое целое. Внутри объекты могут быть устроены сложно, но снаружи из них торчат понятные «ручки» и «индикаторы» — то, через что другие части программы с этими объектами взаимодействуют.
Например, можно представить, что автомобиль — это объект: у него внутри много сложных деталей и связей между ними. Но снаружи всё относительно просто: руль, педали, переключатели, фары, спидометр, тахометр, индикатор температуры, уровня топлива и т. д. Чтобы этим управлять, нам не нужно знать внутреннее устройство машины, а достаточно изучить основные внешние функции.
Одно из понятий ООП — инкапсуляция, и в нём часто применяется такая идея:
доступ к внутренним данным объекта нельзя получить напрямую — это делается через специальные механизмы. Это нужно для того, чтобы другие части программы не могли нештатным образом повлиять на работу объекта.
Если говорить про автомобиль, то внутренние данные объекта — это объём бензина в миллилитрах, который поступает в двигатель на каждом такте работы. Водитель не может управлять этим напрямую и лить в двигатель сколько угодно бензина. Вместо этого он может нажать на педаль газа, а машина дальше сама рассчитает концентрацию бензина в воздушно-топливной смеси.
Такие механизмы доступа к данным через посредника называются геттеры и сеттеры.
Что такое геттер и сеттер
Эти слова пришли из английского от слов get (получать) и set (устанавливать).
Задача геттера — получить данные о чём-то внутри объекта и передать их наружу. Например, водитель не может постоянно заглядывать в бензобак и смотреть, сколько бензина. Вместо этого водитель смотрит на индикатор топлива — тот показывает, сколько бензина осталось в баке. Индикатор топлива — это геттер: он берёт данные из объекта (автомобиль) и отдаёт их тому, кто их попросил (водитель).
Сеттер работает иначе: он меняет значения внутри объекта. В примере с автомобилем педаль газа — это сеттер: в зависимости от силы нажатия она изменяет объём впрыска топлива в двигатель. Так производители защищают двигатель от нештатных ситуаций: даже при нажатии педали газа в пол в двигатель попадёт безопасное количество топлива.
Геттеры и сеттеры в программировании
Чаще всего в программировании эти инструменты используются как метод или интерфейс объекта: вы создаёте объект и указываете, что с ним можно делать. Поясним на примере.
Допустим, мы делаем голосовалку для интернет-магазина — люди выбирают, нравится им какой-то товар или нет. Одна из наших задач — сделать так, чтобы в коде программы ничто не могло напрямую влиять на значение счётчика. Всё, что мы разрешаем, — это увеличить счётчик на единицу или узнать текущее значение. Это избавит нас от ситуации, когда какая-то функция вдруг захочет накрутить счётчик сразу на 10 тысяч голосов. Чтобы так сделать, нам достаточно спрятать переменную счётчика в объект и там же объявить два метода — чтения (узнать результат) и записи (проголосовать, то есть увеличить строго на единицу). Для этого даже необязательно создавать отдельный класс. В JavaScript, например, это можно сделать за счёт стандартного замыкания, когда мы вкладываем одну функцию в другую.
// Объявляем новый объект-константу с помощью стрелочной функции
const createCounter = () => {
// Доступ к этой переменной возможен только внутри этой функции, снаружи поменять её не получится
let count = 0;
// Что возвращает функция
return ({
// Если вызван метод upvote(), увеличиваем переменную счётчика на 1
upvote: () => count += 1,
// Если вызван метод getVoteCount, возвращаем значение счётчика
getVoteCount: () => count.toLocaleString()
});
};
// Создаём новый объект на основе своего
const counter = createCounter();
// Кликаем пару раз, увеличивая счётчик на единицу
// Другим способом мы не сможем его изменить
counter.upvote();
counter.upvote();
// Смотрим, что получилось в итоге (2)
console.log(counter.getVoteCount());
А если в основной программе попробовать поменять значение переменной count, то ничего не изменится. А всё потому, что доступ к переменной count возможен только изнутри объекта и только двумя разрешёнными способами.
// ❌ Кажется, что это тоже сработает, но на самом деле значение счётчика не изменится
counter.count = 100
// На выходе всё так же будет число 2
console.log(counter.getCount());
Зачем они нужны
Основная задача геттеров и сеттеров — защитить данные внутри объекта, чтобы они менялись по своим правилам, а не кто как захочет. Проще говоря, это посредники, которые смотрят, можно что-то сделать с данными или нет. Если можно — делают, если нельзя — нет. В итоге и данные в безопасности, и у пользователя есть инструмент для работы с ними.