Введение

Да, мы все знаем, что это такое из первых двух статей по запросу "Что такое ООП?" или из потоковых лекций первых семестров ВУЗа.

Казалось бы, ООП – Объектно ориентированное программирование. Там что-то про классы, что-то про объекты, если повезет, то, возможно, вспомним, что такое абстракция, инкапсуляция, наследование и полиморфизм. Что там еще надо знать?
И хорошо, если ты прочитал нормальную статью, и не будешь объяснять инкапсуляцию вот так: "ну это история про 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

https://softwareengineering.stackexchange.com/questions/111756/what-is-the-difference-between-layer-of-abstraction-and-level-of-indirection

https://habr.com/ru/post/435900/

https://habr.com/ru/post/87205/

Комментарии (9)


  1. panzerfaust
    29.11.2022 13:41
    +9

    абстракция, инкапсуляция, наследование и полиморфизм

    ...

    Никогда не хотелось копнуть поглубже? Узнать почему у нас есть эти принципы?

    Ваша постановка вопроса похожа на "вам никогда не было интересно, почему у каждого гражданина США есть паспорт США?". Вы путаете направление ассоциации.

    ООП это не закон природы, который мы открыли и пытаемся объяснить. У нас просто есть договоренность, что если язык реализует упомянутые выше фичи, то он реализует ООП. Почему ООП - это именно эти фичи? Потому что так договорились.


    1. jazzdiluffy Автор
      29.11.2022 18:36
      -1

      Здравствуйте! Да, я с Вами полностью согласен, все-таки это статья про то, как можно описать принципы.
      Введение оставляет желать лучшего)

      '''Все остальное – это side effect of indirection. Хотя это и очень громкие слова, поэтому статья и называется "Про ООП через призму косвенности".''' – я упомянул это, но фраза очень теряется среди текста.

      Так или иначе, спасибо большое за комментарий! Любая критика важна, в частности, для пробы пера!


  1. dimas846
    29.11.2022 14:45
    -1

    Как же коробит эта "косвенность"!
    Гугл переводчик какого то рожна переводит indirection как косвенность. Но ему не всегда надо верить. Есть другой переводчик - deepl, он в данном случае справляется лучше: indirection переводит как "перенаправление".


    1. evgenyk
      29.11.2022 14:56
      +2

      Делегирование?


    1. YDR
      29.11.2022 21:53
      +1

      перенаправление это больше redirection, на мой взгляд


  1. oleg_shamshura
    29.11.2022 16:01

    Анекдот про Христа и рыболовный кружок.

    Кто-то когда-то первым догадался в структуру записать адрес подпрограммы...


  1. pae174
    29.11.2022 17:49

    [del]


  1. nin-jin
    30.11.2022 06:13
    -1

    Под косвенностью обычно понимают всё же непрямой доступ в рантайме. Её эквивалент на уровне исходных кодов - та самая абстракция. Яркий пример - макросы. В исходных кодах они добавляют уровень абстракции, но уровень косвенности в рантайме они сами по себе не добавляют.


  1. dikdack
    30.11.2022 10:36

    Спасибо, узнал много интересного.