Введение
Да, мы все знаем, что это такое из первых двух статей по запросу "Что такое ООП?" или из потоковых лекций первых семестров ВУЗа.
Казалось бы, ООП – Объектно ориентированное программирование. Там что-то про классы, что-то про объекты, если повезет, то, возможно, вспомним, что такое абстракция, инкапсуляция, наследование и полиморфизм. Что там еще надо знать?
И хорошо, если ты прочитал нормальную статью, и не будешь объяснять инкапсуляцию вот так: "ну это история про private, данные скрываются!".
Уточним формальности
Что вообще такое объектно-ориентированное программирование? Тут все просто, это значит, что ты строишь программное обеспечение из объектов. Илья, ты что гугл, который на запрос "Кто такой опричник?" отвечает "Тот, кто состоял в рядах опричнины"?
Расслабься, объекты – это такие маленькие машинки, которые живут в компьютере и болтают между собой для выполнения работы.
Прежде чем мы реально поймем зачем ООП, надо разобраться с самым важным – с термином indirection (косвенность). Это ключевой принцип ООП.
Вся суть в косвенности
“All problems in computer science can be solved by another level of indirection.” – David Wheeler
“Most performance problems in computer science can be solved by removing a layer of indirection” – [unknown]
Представь, что ты собираешься позвонить другу и рассказать ему про ООП, но ты не знаешь его номера телефона, ты можешь просто посмотреть его в контактах. Это и есть косвенность.
Тут уже стало понятнее, косвенность означает следующее: вместо того, чтобы использовать значение непосредственно в коде, используй указатель на него. Уже что-то вырисовывается про классы, да?
А теперь представь, что ты купил маме новый чайник, твоя мама живет в городе N, а ты живешь в городе M. Но ты знаешь, что твой друг поедет через 3 дня поездом "город M -> город N". И о чудо! Этот друг живет в квартире напротив твоей мамы. Осталось просто отдать ему чайник и вежливо попросить передать его маме.
Собственно, вот и другое значение косвенности – вместо того, чтобы делать работу самому, просто попроси сделать ее кого-то другого ?
У косвенности есть уровни. Тут тоже все просто, ты пришел в магазин, тебе пробили товар с желтым ценником по его обычной цене, ты говоришь, кассиру (layer 1), чтобы убрал этот товар, а кассир, в свою очередь, ведет тебя к Гале, которая умеет делать отмену (layer 2). В этом плане компьютер терпеливый, его можно гонять из места в место в поисках нужной инстанции :)
Переменные и косвенность
Внимательный читатель уже понял, что использовал косвенность уже десятки тысяч раз. А кто не понял, тот поймет.
Есть у нас вот такая функция, где мы гордо говорим про числа от 1 до 5, а потом печатаем их.
int main(int argc, const char *argv[]) {
NSLog(@"The numbers from 1 to 5:");
for (int i = 1; i <= 5; i++) {
NSLog(@"%d\n", i);
}
return 0;
}
К нам приходят и говорят, что теперь надо печатать числа от 1 до 10. Лезем в код, меняем в двух местах пятерку на десятку.
Потом к нам приходят и говорят, что теперь надо от 1 до 100, мы устаем и применяем новый изученный скилл – косвенность – выносим информацию о верхней границе в отдельную переменную count.
int main(int argc, const char * argv[]){
int count = 100;
NSLog(@"The numbers from 1 to %d:", count);
for (int i = 1; i <= count; i++) {
NSLog(@"%d\n", i);
}
return 0;
}
Теперь код надо трогать только в одном месте. Стало намного лучше.
Косвенность через файлы
Вы с ребятами забацали новый взрывной стартап – вы запускаете сервис, который считает сколько в строке символов. Осталось только найти инвесторов!
Первая версия выглядит следующим образом:
int main(int argc, const char * argv[]) {
const char *strs[4] = { "Objective-C", "Swift", "C++", "Go" };
int strsCount = 4;
for (int i = 0; i < strsCount; i++) {
NSLog(@"%s is %lu characters long", strs[i], strlen(strs[i]));
}
return 0;
}
Приходят венчурные капиталисты и говорят, что наш стартап будет идеальным для киберспорта, они предлагают узнавать сколько символов в строке формата:
Имя "никнейм" Фамилия
Понимаете к чему это сводится? strs будет выглядеть следующим образом:
const char *strs[4] = {
"Alexander \"TORONTOTOKYO\" Khertek",
"Ilya \"Yatoro\" Mularchuk",
"Danil \"Dendi\" Ishutin",
"Ilya \"ALOHADANCE\" Korobkin" };
Извините меня, это плохо читаемо, здесь легко ошибиться, трудно что-то исправить, а добавить условно еще 20 игроков – довольно большой объем работы...
А если венчурным капиталистам захочется считать сколько символов в строке формата:
Блюдо "Салат "Альбина" Очень вкусно!". Время приготовления – "35 минут".
"Блюдо \"Салат \"Альбина\" Очень вкусно!\". Время приготовления – \"35 минут\".
Я сошел с ума... Хочется избежать этого ада, и снова на помощь приходит косвенность, вынесем это в файл. Сразу заметим, что можно считывать напрямую, указывая путь к файлу.
FILE *wordFile = fopen("/tmp/words.txt", "r");
Проблему это решит, но мы помним – "вместо того, чтобы использовать значение непосредственно в коде, используй указатель на него", поэтому сделаем еще лучше:
int main(int argc, const char * argv[]) {
if (argc == 1) {
NSLog(@"you need to provide a file name");
return 1;
}
FILE *strsFile = fopen(argv[1], "r");
char str[100];
while (fgets(str, 100, strsFile)) {
str[strlen(str) - 1] = '\0';
NSLog(@"%s is %lu characters long", str, strlen(str));
}
fclose(strsFile);
return 0;
}
Теперь можно создать кучу файлов по каждому запросу инвесторов: и киберспорт, и кулинария, и названия таблеток, и т.д.
Теперь программист должен сделать только что-то подобное:
$ clang -framework Foundation -o charactersCounter ./main.m
А менеджер может плодить файлики, заполнять их в текстовом редакторе без всяких запятых, слэшей и других трудностей, не обременяя лишней работой разработчика.
А потом просто дергать исполняемый файл
$ ./charactersCounter /tmp/cyber.txt
Косвенность и ООП
Пора бы и к выводам перейти.
Объектно-ориентированное программирование - это все про косвенность.
ООП использует косвенность для доступа к данным, так же как мы делали это в предыдущих примерах, используя переменные, файлы и аргументы.
По-настоящему революция ООП заключается в том, что этот подход использует косвенность для вызова кода. Вместо того чтобы вызывать функцию напрямую, вы в конечном итоге вызываете ее косвенно.
Все остальное – это side effect of indirection. Хотя это и очень громкие слова, поэтому статья и называется "Про ООП через призму косвенности".
Абстракция реализуется с помощью косвенности.
Например, виртуальная память: Это непрерывное адресное пространство, полностью находящееся в вашем распоряжении. Эта абстракция реализована с помощью косвенности через таблицу страниц, которая переводит виртуальные адреса в физические.
Чтобы добавить уровень абстракции, необходимо добавить уровень косвенности. Но добавление косвенности не обязательно дает абстракцию. Обычные необходимое и достаточное условия.
Инкапсуляция — собирание в одном месте знания, относящиеся к устройству некой сущности, правилам обращения и операциям с ней. Или организация слоев косвенности таким образом, чтобы собрать в "капсулу" все знания о той или иной сущности. Та самая Галя, которая умеет делать отмену – только она знает какой пин-код ввести, и никому она этот пин-код не расскажет, но Галю можно попросить прийти и ввести его.
Наследование – это свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью.
И снова слои, и снова объясню на Гале. Тут совсем просто
class GalyaCashier: DefaultCashier
То есть Галя умеет делать все, что умеют обычные кассиры, но moreover Галя умеет отмену.
Полиморфизм – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.
А тут на Гале понять можно? Конечно)
Представим, что в конце дня каждый сотрудник магазина должен написать отчет. Т.е. – есть какой-то интерфейс с методом writeReport()
Только вот DefaultCashier будет писать:
"За сегодня я пробил 120 товаров"
А GalyaCashier будет писать:
"За сегодня я пробила 100 товаров и сделала 3 отмены"
Достаточно заменить в вашей системе объект одного класса на объект другого класса, и результат будет достигнут.
Заключение
Спасибо большое всем за внимание! Вообще каждый в праве понимать как хочет, но именно это объяснение запало мне в душу. Все совпадения с Галями случайны, я очень благодарен за то, что это стало для меня синонимом к "слой косвенности".
Источники
Learn Objective-C on the Mac, Authors: Mark Dalrymple, Scott Knaster
https://medium.com/@nmckinnonblog/indirection-fba1857630e2