Если видео недоступно для просмотра, попробуйте выключить блокировщик рекламы.

Абстракции позволяют нам моделировать необходимую предметную область. Попробуем понять это утверждение на конкретном примере. Допустим, мы хотим сделать программу-каталог для управления коллекцией книг. Вы можете сразу кинуться писать код, но так делать не стоит :)

В первую очередь необходимо проанализировать предметную область (книги + каталоги) и сформировать базовую модель. Нужно начать с определения сущностей и операций над ними. В случае нашего каталога у нас есть по крайней мере две сущности: книга и коллекция. Базовыми операциями сделаем добавление книги в коллекцию и удаление её из коллекции.

Напишем код операции для добавления сущности в коллекцию:

$book1 = makeBook('Дети капитана Гранта');
$book2 = makeBook('Приключения Тома Сойера');
$coll = makeColl('приключения');
$updatedColl1 = addBookToColl($coll, $book1);
$updatedColl2 = addBookToColl($updatedColl1, $book2);

Мы создаём две книги, одну коллекцию и добавляем эти книги в коллекцию. Что значит "создали книгу"? Что такое "книга"? Книга — это сущность, которая ведёт себя как книга. Что это значит?

Так как мы работаем с языком программирования, то конструктор makeBook, создающий книгу, возвращает какие-то данные: примитивные (число, строку) или составные, используя пары. Главное — нам не важно, как они устроены.

Организовать эти данные можно тысячью разных способов, и конкретный способ зависит от разработчика, его предпочтений и данных, которые необходимо хранить. То, что книга является книгой, определяется не её внутренним устройством, а тем, что к ней применим набор операций, созданный для книг. Например, операция «получить имя книги». Сами операции знают, как устроены данные, иначе они не смогли бы ими манипулировать. Запомните: это детали реализации. Не заглянув внутрь операций, мы не узнаем, как они устроены, но нам это и не нужно.

В этом и заключается вся суть абстракции. Мы знаем, как создать сущность и какие операции к ней применимы. Обычно это и называется дизайном кода, а сами операции + конструктор — это API или интерфейс модуля/пакета/библиотеки.

Получаем следующий алгоритм:

  1. Анализируем предметную область. Выделяем сущности.
  2. Реализуем конструктор. Внутреннее представление сущностей выбираем на основе того, что мы планируем в них хранить.
  3. Реализуем необходимые операции.

В примере с книгами видно, что мы храним только название. А это значит, что нам достаточно хранить одну строчку.

Тогда реализация конструктора будет такой:

function makeBook($name){
  return $name;
}

$book = makeBook('Дети капитана Гранта');
print_r(toString($book)); // => 'Дети капитана Гранта'' -?-

А что, если мы захотим дополнительно хранить версию издания книги? Тогда мы можем использовать пары для хранения составных данных, и у нас уже появляются составные данные, так как нужно хранить два параметра.

function makeBook($name, $rev)
{
  return cons($name, $rev);
}

$book = makeBook('Дети капитана Гранта', 3);
print_r(toString($book)); // => ('Дети капитана Гранта', 3);

Поменяется ли клиентский код, использующий нашу библиотеку, при изменении внутреннeй структуры? Как видно из примеров выше, ничего не поменяется, кроме вызова конструкторов. Это и есть хорошая абстракция, при которой наш код не рассыпается в случае любых изменений внутренней реализации.

Примеры других абстракций

Пример с книгами — это всего лишь один из многих вариантов. Программисты в своей работе постоянно перекладывают сущности реального мира на код, создавая всевозможные абстракции данных. Давайте попробуем пофантазировать и накидать еще вариантов. Я добавлю только три, а еще три додумайте сами. В реальной жизни абстракции будут сложнее, так как должны включать больше данных, но суть от этого не поменяется.

  1. Товар. Всё, что продаётся и покупается. Предположим, что нам интересны только два параметра: цена и название. Саму сущность можно назвать $product. Тогда наша абстракция будет содержать как минимум три следующих функции:

    // интерфейсные функции (абстракция)
    function makeProduct($name, $cost)
    {
      return cons($name, $cost);
    }
    
    function getName($product)
    {
      return car($product);
    }
    
    function getCost($product)
    {
      return cdr($product);
    }
    
    // использование
    $product = makeProduct('Сыр Пармезан', 100);
    getName($product); // 'Сыр Пармезан'
    getCost($product); // 100
    
  2. Текстовый документ. Например, как в Google Docs. В нашем примере он будет состоять из названия и содержимого.

    // интерфейсные функции (абстракция)
    function makeDocument($name, $body)
    {
      return cons($name, $body);
    }
    
    function getName($document)
    {
      return car($document);
    }
    
    function getBody($document)
    {
      return cdr($document);
    }
    
    // использование
    $document = makeDocument('Как я провел лето', 'Много текста');
    getName($document); // 'Как я провел лето'
    getBody($document); // 'Много текста'
    
  3. А вот пример, в котором нужно хранить не два значения, а три. Это точка в пространстве.

    // интерфейсные функции (абстракция)
    function make3dPoint($x, $y, $z)
    {
      return cons(cons($x, $y), $z); // альтернатива cons($x, cons($y, $z));
    }
    
    function getX($point)
    {
      return car(car($point));
    }
    
    function getY($point)
    {
      return cdr(car($point));
    }
    
    function getZ($point)
    {
      return cdr($point);
    }
    
    // использование
    $point = make3dPoint(1, 10, -3);
    

И теперь самое интересное. Любая абстракция, построенная таким образом, может выступать в роли строительных блоков в другой абстракции.

Предположим, что мы продаём документы как продукты:

$document = makeDocument('Как я провел лето', 'Много текста');
$product = makeProduct($document, 100);

А дальше всё по аналогии.

Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Node, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →