Абстракции позволяют нам моделировать необходимую предметную область. Попробуем понять это утверждение на конкретном примере. Допустим, мы хотим сделать программу-каталог для управления коллекцией книг. Вы можете сразу кинуться писать код, но так делать не стоит :)
В первую очередь необходимо проанализировать предметную область (книги + каталоги) и сформировать базовую модель. Нужно начать с определения сущностей и операций над ними. В случае нашего каталога у нас есть по крайней мере две сущности: книга и коллекция. Базовыми операциями сделаем добавление книги в коллекцию и удаление её из коллекции.
Напишем код операции для добавления сущности в коллекцию:
var book1 = makeBook("Дети капитана Гранта");
var book2 = makeBook("Приключения Тома Сойера");
var coll = makeColl("приключения");
var updatedColl1 = addBookToColl(coll, book1);
var updatedColl2 = addBookToColl(updatedColl1, book2);
Мы создаём две книги, одну коллекцию и добавляем эти книги в коллекцию. Что значит "создали книгу"? Что такое "книга"? Книга — это сущность, которая ведёт себя как книга. Что это значит?
Так как мы работаем с языком программирования, то конструктор makeBook
, создающий книгу, возвращает какие-то данные: примитивные (число, строку) или составные, используя пары. Главное — нам не важно, как они устроены.
Организовать эти данные можно тысячью разных способов, и конкретный способ зависит от разработчика, его предпочтений и данных, которые необходимо хранить. То, что книга является книгой, определяется не её внутренним устройством, а тем, что к ней применим набор операций, созданный для книг. Например, операция «получить имя книги». Сами операции знают, как устроены данные, иначе они не смогли бы ими манипулировать. Запомните: это детали реализации. Не заглянув внутрь операций, мы не узнаем, как они устроены, но нам это и не нужно.
В этом и заключается вся суть абстракции. Мы знаем, как создать сущность и какие операции к ней применимы. Обычно это и называется дизайном кода, а сами операции + конструктор — это API или интерфейс модуля/пакета/библиотеки.
Получаем следующий алгоритм:
В примере с книгами видно, что мы храним только название. А это значит, что нам достаточно хранить одну строчку.
Тогда реализация конструктора будет такой:
static String makeBook(String name) {
return name;
}
var book = makeBook("Дети капитана Гранта");
System.out.println(bookToString(book)); // => "Дети капитана Гранта"
А что, если мы захотим дополнительно хранить версию издания книги? Тогда мы можем использовать пары для хранения составных данных, и у нас уже появляются составные данные, так как нужно хранить два параметра.
static Pair makeBook(String name, int rev) {
return cons(name, rev);
}
var book = makeBook("Дети капитана Гранта", 3);
System.out.println(bookToString(book)); // => ("Дети капитана Гранта", 3);
Поменяется ли клиентский код, использующий нашу библиотеку, при изменении внутренней структуры? Как видно из примеров выше, ничего не поменяется, кроме вызова конструкторов. Это и есть хорошая абстракция, при которой наш код не рассыпается в случае любых изменений внутренней реализации.
Пример с книгами — это всего лишь один из многих вариантов. Программисты в своей работе постоянно перекладывают сущности реального мира на код, создавая всевозможные абстракции данных. Давайте попробуем пофантазировать и накидать ещё вариантов. Я добавлю только три, а ещё три додумайте сами. В реальной жизни абстракции будут сложнее, так как должны включать больше данных, но суть от этого не поменяется.
Товар. Всё, что продаётся и покупается. Предположим, что нам интересны только два параметра: цена и название. Саму сущность можно назвать product
. Тогда наша абстракция будет содержать как минимум три следующих функции:
// интерфейсные функции (абстракция)
static Pair makeProduct(String name, double cost) {
return cons(name, cost);
}
static String getName(Pair product) {
// здесь и ниже мы не рассматриваем приведение типов, которое может понадобиться в некоторых случаях.
return car(product);
}
static double getCost(Pair product) {
return cdr(product);
}
// использование
var product = makeProduct("Сыр Пармезан", 100);
getName(product); // "Сыр Пармезан"
getCost(product); // 100.0
Текстовый документ. Например, как в Google Docs. В нашем примере он будет состоять из названия и содержимого.
// интерфейсные функции (абстракция)
static Pair makeDocument(String name, String body) {
return cons(name, body);
}
static Pair getName(Pair document) {
return car(document);
}
static Pair getBody(Pair document) {
return cdr(document);
}
// использование
var document = makeDocument("Как я провёл лето", "Много текста");
getName(document); // "Как я провёл лето"
getBody(document); // "Много текста"
А вот пример, в котором нужно хранить не два значения, а три. Это точка в пространстве.
// интерфейсные функции (абстракция)
static Pair make3dPoint(double x, double y, double z) {
return cons(cons(x, y), z); // альтернатива cons(x, cons(y, z));
}
static double getX(Pair point) {
return car(car(point));
}
static double getY(Pair point) {
return cdr(car(point));
}
static double getZ(Pair point) {
return cdr(point);
}
// использование
var point = make3dPoint(1, 10, -3);
И теперь самое интересное. Любая абстракция, построенная таким образом, может выступать в роли строительных блоков в другой абстракции.
Предположим, что мы продаём документы как продукты:
var document = makeDocument("Как я провёл лето", "Много текста");
var product = makeProduct(document, 100);
А дальше всё по аналогии.
Вам ответят команда поддержки Хекслета или другие студенты.
Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.
Загляните в раздел «Обсуждение»:
Статья «Ловушки обучения»
Вебинар «Как самостоятельно учиться»
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Наши выпускники работают в компаниях:
Зарегистрируйтесь или войдите в свой аккаунт