В этом уроке мы начнём проектирование библиотек для работы с графическими примитивами (точками, отрезками), а также фигурами (кругами, прямоугольниками). Естественно, проще всего начать с основы — библиотеки для работы с точками. Для этого давайте вспомним координатную плоскость и то, как определяются на ней точки.
В этом примере мы видим плоскость, на которой расположены две оси (ось абсцисс X и ось ординат Y) и точки. При этом обычно точка описывается двумя параметрами: первым указывается координата x (абсцисса), вторым — координата y (ордината). Например, точка D описывается как (2, 4), точка C — (1, 0) и так далее. Координаты могут быть отрицательными. Оси делят плоскость на четыре части, которые называются квадрантами. На рисунке точка D лежит в первом квадранте, точки A, G, E — во втором, в третьем и в четвёртом квадрантах соответственно.
Когда мы говорим о точках, мы на самом деле имеем в виду так называемые абстрактные данные. Это означает, что мы не делаем никаких предположений (кроме самых необходимых) для того, чтобы рассуждать в терминах этих данных (в нашем случае — точек). И действительно, вспомним, например, любой раздел школьной математики: когда мы оперируем какими-то понятиями, мы вообще не думаем о том, что они должны быть как-то представлены, мы мыслим в них на абстрактном уровне. Это позволяет нам строить законы, делать выводы и предположения о том, как будут вести себя данные, как они друг с другом комбинируются. При этом нам не нужна никакая реализация, нам не нужен ни язык программирования, ни компьютер. Более того, нам вообще необязательно графически изображать эти данные — мы можем представлять это в голове и при этом делать выводы, имеющие практическое значение. При этом конкретное представление абстрактных данных (так называемые конкретные данные) определяется условиями прикладного использования, например, особенностями разных языков программирования. Зачастую библиотеки, которые снаружи выглядят одинаково и оперируют одними и теми же понятиями, внутри устроены совершенно по-разному. Естественно, при манипуляциях с такими библиотеками необходимо оставаться в рамках понятийного аппарата тех абстрактных данных, с которыми мы работаем. "Клеем" между абстрактными данными и конкретными данными выступает так называемый интерфейс. Интерфейс — это набор функций, обычно разделяемых на конструкторы и селекторы. Конструкторы позволяют из набора данных строить составной объект, а с помощью селекторов из составного объекта извлекают его части. В нашем случае, если говорить о точках, селекторы позволяют извлекать координаты x и y. Вот как выглядит интерфейс нашей библиотеки:
В коде выше видно, что мы определили функцию makePoint, которая принимает на вход два числа (x и y) и возвращает точку с такими координатами. Кроме этого, есть два основных селектора getX и getY, которые возвращают соответствующие координаты из точки.
При проектировании библиотеки мы используем стратегию "мечтать не вредно", потому что ещё не знаем, как она будет устроена внутри. Это, по большому счету, не имеет значения, потому что мы пользуемся абстрактными данными и оперируем терминами нашей предметной области, а не конкретной реализации. Поэтому мы можем сосредоточиться на сути, отложив принятие решения о деталях.
Кроме основных селекторов, нами определена функция pointToString, которая возвращает текстовое представление точки и может быть использована для отладки.
Давайте посмотрим, какие манипуляции можно производить над точками, имея базовый интерфейс:
В примере выше определена функция symmetricalXPoint, которая возвращает точку, симметричную заданной относительно оси X. Например, для точки c координатами (10, 10) функция вернёт точку с координатами (10, -10). Функция устроена следующим образом: с помощью селекторов мы извлекаем координаты, инвертируем координату Y и создаём новую точку с помощью конструктора makePoint.
Большинство операций с точками состоят в том, чтобы извлечь координаты, произвести с ними необходимые изменения и создать новые точки. С помощью этого нехитрого алгоритма можно реализовать массу других функций, например:
Функция quadrant определяет квадрант, в котором расположена точка, а функция distance вычисляет расстояние между заданными точками. При этом мы оперируем понятием "точки", а обращение к составным частям этих точек происходит внутри и скрыто от наших глаз.
Подведём итог:
Абстрактными данными у нас является точка, которая характеризуется двумя значениями (координатами) x и y.
Конкретные данные: на текущий момент мы не знаем, как реализованы точки (что не мешает нам пользоваться библиотекой в рамках нашей предметной области).
Интерфейс:
Внутренняя реализация
Если вас мучает вопрос, что находится внутри переменной point, скажу сразу — мы ответим на него в следующих уроках. Для работы с точками не нужно знать их устройство. Когда мы решаем геометрические задачи, то легко оперируем понятием отрезка, круга и других фигур. С помощью формул мы можем находить площадь, рассчитывать расстояние между точками, проводить касательные и делать многое другое.
Каждое из перечисленных выше понятий абстрактно, не имеет прямого воплощения в реальной жизни и может применяться как к описанию детских игрушек (круглый мяч), так и египетских пирамид. Причём в каждом конкретном случае нас будут интересовать разные детали. В одних случаях важны только размеры, в других — расположение относительно некоей системы координат, в третьих — цвет фигуры.
В некоторых задачах при работе с точками могут отсутствовать координаты, но имеются цвет или скорость мерцания. Тогда набор функций для работы с подобными точками будет кардинально отличаться от нашего набора.
Точки, которые мы рассматриваем в этом курсе, интересны для нас только как место на координатной плоскости, задаваемое парой чисел — координат. Важно, что любая пара чисел однозначно определяет точку на этой плоскости, и существование двух разных точек с одинаковыми координатами невозможно.
Точки — это всего лишь один из примеров того, как мы пытаемся выразить в коде нашу предметную область, в данном случае — геометрию. Подобная абстракция используется в исходном коде графических редакторов. В финансовых приложениях используется понятие "деньги". Деньги можно выразить просто числом, но этого наверняка будет недостаточно, потому что помимо численного значения, для выражения денежной суммы необходимо как минимум указать ещё и валюту.
Можно сказать, что в коде выше создан свой собственный, пользовательский тип данных. В отличие от примитивных типов данных, таких как числа и строки, наш новый тип является составным — отсюда и понятие составные данные. Внутри себя он хранит набор значений, но мы оперируем ими как единым целым.
Реальные прикладные приложения, по большей части, оперируют именно такими данными. Например, в Хекслете это уроки, курсы, практики, пользователи, наставники, топики в сообществе, счета и сотни других понятий. Все они гораздо более сложные, чем те же точки. В одном пользователе могут находиться десятки параметров.
Невозможно заранее определить типы данных для представления всего сущего на земле. Всё, что требуется от языка — предоставить возможность определять свои собственные типы и операции над ними. И об этом мы поговорим в следующем уроке.
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 16:59:31 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="OKxEpbhOgX0GGVKi3Tt-1B3JWKCmof0-QX8Lw_uMkPjXfY-SSjAsHbBadjrRNI6j3cB1Cq6WA5z8n5GXqYt3lg";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Точки | Java: Составные данные</title>
<meta name="description" content="Точки / Java: Составные данные: Начинаем проектирование примитивной графической библиотеки с создания специальных данных, которые будут моделировать точки на плоскости">
<link rel="canonical" href="https://ru.hexlet.io/courses/java-compound-data/lessons/points/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Точки">
<meta property="og:title" content="Java: Составные данные">
<meta property="og:description" content="Точки / Java: Составные данные: Начинаем проектирование примитивной графической библиотеки с создания специальных данных, которые будут моделировать точки на плоскости">
<meta property="og:url" content="https://ru.hexlet.io/courses/java-compound-data/lessons/points/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="K76bCD5P2Tozy0trD1cft4L2zDrnUOH4x7XQuvcWwBHEb1A_zDF0WoWIb_MDWO_AQv_hkO9nH1p6VUrupREnfw" />
<script src="/vite/assets/inertia-INZxX8jp.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-nkZBEvfU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-6pOtQ3OW.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDAxNiwicHVyIjoiYmxvYl9pZCJ9fQ==--eb66b9b5e26fafa32844ce0f4522c3ed84544040/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T16:59:31.677Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"ARlxHLIM-Sc1MUr_U5tJXc-SQmxchlC-FaFHo3_7djLuyLorQHJUR4NybmdflLkqD5tvxlSxrhyoQd33LfyRXA","topics":[{"id":42341,"title":"Почему в решение учителя в одной функции/методе используется `double`, а в другой `var`? В этом есть какой-то смысл?","plain_title":"Почему в решение учителя в одной функции/методе используется double, а в другой var? В этом есть какой-то смысл? ","creator":{"public_name":"","id":267871,"is_tutor":false},"comments":[{"creator":{"public_name":"Ильнар Шафигуллин","id":210551,"is_tutor":false},"id":92104,"body":"В данном случае это одно и то же, т.к. система выведения типов при компиляции заменит var на соответствующий тип. Когда мы пишем `double` мы сами определяем тип, а когда `var`, то оставляем право выбора за компилятором.\n\nНо спасибо что указали, сейчас поправлю, чтобы везде было одинаково.","topic_id":42341}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Точки","entity_url":null,"active":true}},{"id":76042,"title":"Не проходит 1 тест для метода calculateDistance():\n\ntestCalculateDistance() ✘ \nexpected: 2.0\nbut was: 7.280109889280518\n\nЕсли заглянуть в сам тест, то expected как раз 7.280109889280518.\n\n[https://ru.hexlet.io/code_reviews/735180](https://ru.hexlet.io/code_reviews/735180)","plain_title":"Не проходит 1 тест для метода calculateDistance(): testCalculateDistance() ✘ expected: 2.0 but was: 7.280109889280518 Если заглянуть в сам тест, то expected как раз 7.280109889280518. https://ru.hexlet.io/code_reviews/735180 (https://ru.hexlet.io/code_reviews/735180) ","creator":{"public_name":"Sabrina","id":525565,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":158288,"body":"Оу, тут и правда перепутаны ожидаемое и фактически полученное значение в тесте. Поправил тест, спасибо! Нужно будет нажать Сброс, чтобы получить новую версию упражнения. Ожидается получить 7.280109889280518, а фактически получено 2","topic_id":76042}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Точки","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":1193,"slug":"java_compound_data_points_exercise","name":null,"state":"active","kind":"exercise","language":"java","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"## main/hexlet/LessonPoints.java\n\nРеализуйте и экспортируйте следующие функции для работы с точками:\n\n* `getQuadrant` — функция, которая вычисляет квадрант, в котором находится точка.\nНиже приведена схема, показывающая номера квадрантов на плоскости.\n\n ```\n +\n 2 | 1\n |\n +----------------+\n |\n 3 | 4\n +\n ```\n\n ```java\n var point = makePoint(1, 5);\n getQuadrant(point); // 1\n getQuadrant(makePoint(3, -3)); // 4\n ```\n\n Если точка не принадлежит ни одному квадранту (т.е., если она лежит хотя бы на одной из осей координат), то функция должна возвращать 0:\n\n ```java\n var point = makePoint(0, 7);\n getQuadrant(point); // 0\n getQuadrant(makePoint(2, 0)); // 0\n ```\n\n* `getSymmetricalPoint` — функция, возвращающая новую точку, симметричную относительно начала координат.\nТакая симметричность означает, что меняются знаки у `x` и `y`.\n\n ```java\n getSymmetricalPoint(makePoint(1, 5)); // makePoint(-1, -5)\n ```\n\n* `calculateDistance` — функция, вычисляющая расстояние между точками по формуле: `d = sqrt((x2−x1)^2+(y2−y1)^2)`\n\n ```java\n calculateDistance(makePoint(-2, -3), makePoint(-4, 4)); // ≈ 7.28\n ```\n","prepared_readme":"## main/hexlet/LessonPoints.java\n\nРеализуйте и экспортируйте следующие функции для работы с точками:\n\n* `getQuadrant` — функция, которая вычисляет квадрант, в котором находится точка.\nНиже приведена схема, показывающая номера квадрантов на плоскости.\n\n ```\n +\n 2 | 1\n |\n +----------------+\n |\n 3 | 4\n +\n ```\n\n ```java\n var point = makePoint(1, 5);\n getQuadrant(point); // 1\n getQuadrant(makePoint(3, -3)); // 4\n ```\n\n Если точка не принадлежит ни одному квадранту (т.е., если она лежит хотя бы на одной из осей координат), то функция должна возвращать 0:\n\n ```java\n var point = makePoint(0, 7);\n getQuadrant(point); // 0\n getQuadrant(makePoint(2, 0)); // 0\n ```\n\n* `getSymmetricalPoint` — функция, возвращающая новую точку, симметричную относительно начала координат.\nТакая симметричность означает, что меняются знаки у `x` и `y`.\n\n ```java\n getSymmetricalPoint(makePoint(1, 5)); // makePoint(-1, -5)\n ```\n\n* `calculateDistance` — функция, вычисляющая расстояние между точками по формуле: `d = sqrt((x2−x1)^2+(y2−y1)^2)`\n\n ```java\n calculateDistance(makePoint(-2, -3), makePoint(-4, 4)); // ≈ 7.28\n ```\n","has_solution":true,"entity_name":"Точки"},"units":[{"id":3781,"name":"theory","url":"/courses/java-compound-data/lessons/points/theory_unit"},{"id":3782,"name":"quiz","url":"/courses/java-compound-data/lessons/points/quiz_unit"},{"id":3783,"name":"exercise","url":"/courses/java-compound-data/lessons/points/exercise_unit"}],"links":[{"id":424285,"name":"Система координат","url":"https://ru.wikipedia.org/wiki/Система_координат"}],"ordered_units":[{"id":3781,"name":"theory","url":"/courses/java-compound-data/lessons/points/theory_unit"},{"id":3782,"name":"quiz","url":"/courses/java-compound-data/lessons/points/quiz_unit"},{"id":3783,"name":"exercise","url":"/courses/java-compound-data/lessons/points/exercise_unit"}],"id":1737,"slug":"points","state":"approved","name":"Точки","course_order":20,"goal":"Начинаем проектирование примитивной графической библиотеки с создания специальных данных, которые будут моделировать точки на плоскости","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"В этом уроке мы начнём проектирование библиотек для работы с графическими примитивами (точками, отрезками), а также фигурами (кругами, прямоугольниками). Естественно, проще всего начать с основы — библиотеки для работы с точками. Для этого давайте вспомним координатную плоскость и то, как определяются на ней точки.\n\n\n\nВ этом примере мы видим плоскость, на которой расположены две оси (ось абсцисс X и ось ординат Y) и точки. При этом обычно точка описывается двумя параметрами: первым указывается координата x (абсцисса), вторым — координата y (ордината). Например, точка D описывается как (2, 4), точка C — (1, 0) и так далее. Координаты могут быть отрицательными. Оси делят плоскость на четыре части, которые называются квадрантами. На рисунке точка D лежит в первом квадранте, точки A, G, E — во втором, в третьем и в четвёртом квадрантах соответственно.\n\nКогда мы говорим о точках, мы на самом деле имеем в виду так называемые __абстрактные данные__. Это означает, что мы не делаем никаких предположений (кроме самых необходимых) для того, чтобы рассуждать в терминах этих данных (в нашем случае — точек). И действительно, вспомним, например, любой раздел школьной математики: когда мы оперируем какими-то понятиями, мы вообще не думаем о том, что они должны быть как-то представлены, мы мыслим в них на абстрактном уровне. Это позволяет нам строить законы, делать выводы и предположения о том, как будут вести себя данные, как они друг с другом комбинируются. При этом нам не нужна никакая реализация, нам не нужен ни язык программирования, ни компьютер. Более того, нам вообще необязательно графически изображать эти данные — мы можем представлять это в голове и при этом делать выводы, имеющие практическое значение. При этом конкретное представление абстрактных данных (так называемые __конкретные данные__) определяется условиями прикладного использования, например, особенностями разных языков программирования. Зачастую библиотеки, которые снаружи выглядят одинаково и оперируют одними и теми же понятиями, внутри устроены совершенно по-разному. Естественно, при манипуляциях с такими библиотеками необходимо оставаться в рамках понятийного аппарата тех абстрактных данных, с которыми мы работаем. \"Клеем\" между абстрактными данными и конкретными данными выступает так называемый интерфейс. Интерфейс — это набор функций, обычно разделяемых на конструкторы и селекторы. Конструкторы позволяют из набора данных строить составной объект, а с помощью селекторов из составного объекта извлекают его части. В нашем случае, если говорить о точках, селекторы позволяют извлекать координаты x и y. Вот как выглядит интерфейс нашей библиотеки:\n\n```java\nvar x = 5;\nvar y = -7;\n\n// Конструктор\nvar point = makePoint(x, y);\n\n// Селекторы\ngetX(point);\ngetY(point);\n\npointToString(point); // (5, -7)\n```\n\nВ коде выше видно, что мы определили функцию `makePoint`, которая принимает на вход два числа (`x` и `y`) и возвращает точку с такими координатами. Кроме этого, есть два основных селектора `getX` и `getY`, которые возвращают соответствующие координаты из точки.\nПри проектировании библиотеки мы используем стратегию \"мечтать не вредно\", потому что ещё не знаем, как она будет устроена внутри. Это, по большому счету, не имеет значения, потому что мы пользуемся абстрактными данными и оперируем терминами нашей предметной области, а не конкретной реализации. Поэтому мы можем сосредоточиться на сути, отложив принятие решения о деталях.\n\nКроме основных селекторов, нами определена функция `pointToString`, которая возвращает текстовое представление точки и может быть использована для отладки.\n\nДавайте посмотрим, какие манипуляции можно производить над точками, имея базовый интерфейс:\n\n```java\npublic static Pair symmetricalXPoint(Pair point) {\n return makePoint(getX(point), -getY(point));\n}\n\nvar point = makePoint(10, 10);\n\npointToString(symmetricalXPoint(point)); // (10, -10)\n```\n\nВ примере выше определена функция `symmetricalXPoint`, которая возвращает точку, симметричную заданной относительно оси X. Например, для точки c координатами (10, 10) функция вернёт точку с координатами (10, -10). Функция устроена следующим образом: с помощью селекторов мы извлекаем координаты, инвертируем координату Y и создаём новую точку с помощью конструктора `makePoint`.\nБольшинство операций с точками состоят в том, чтобы извлечь координаты, произвести с ними необходимые изменения и создать новые точки. С помощью этого нехитрого алгоритма можно реализовать массу других функций, например:\n\n```java\nvar point1 = makePoint(3, 4);\nvar point2 = makePoint(0, 0);\n\nquadrant(point1); // 1\ndistance(point1, point2); // 5\n```\n\nФункция `quadrant` определяет квадрант, в котором расположена точка, а функция `distance` вычисляет расстояние между заданными точками. При этом мы оперируем понятием \"точки\", а обращение к составным частям этих точек происходит внутри и скрыто от наших глаз.\n\nПодведём итог:\n\nАбстрактными данными у нас является точка, которая характеризуется двумя значениями (координатами) `x` и `y`.\n\nКонкретные данные: на текущий момент мы не знаем, как реализованы точки (что не мешает нам пользоваться библиотекой в рамках нашей предметной области).\n\nИнтерфейс:\n\n```java\n// Конструктор\nmakePoint(x, y);\n\n// Селекторы\ngetX(point);\ngetY(point);\n```\n\n---\n\n## Внутренняя реализация\n\n```java\nvar point = makePoint(4, 5);\n\nSystem.out.println(getX(point));\nSystem.out.println(getY(point));\n\nSystem.out.println(point); // => 'don't do it'\nSystem.out.println(pointToString(point));\nSystem.out.println(quadrant(point));\n```\n\nЕсли вас мучает вопрос, что находится внутри переменной `point`, скажу сразу — мы ответим на него в следующих уроках. Для работы с точками не нужно знать их устройство. Когда мы решаем геометрические задачи, то легко оперируем понятием отрезка, круга и других фигур. С помощью формул мы можем находить площадь, рассчитывать расстояние между точками, проводить касательные и делать многое другое.\n\nКаждое из перечисленных выше понятий абстрактно, не имеет прямого воплощения в реальной жизни и может применяться как к описанию детских игрушек (круглый мяч), так и египетских пирамид. Причём в каждом конкретном случае нас будут интересовать разные детали. В одних случаях важны только размеры, в других — расположение относительно некоей системы координат, в третьих — цвет фигуры.\n\nВ некоторых задачах при работе с точками могут отсутствовать координаты, но имеются цвет или скорость мерцания. Тогда набор функций для работы с подобными точками будет кардинально отличаться от нашего набора.\n\nТочки, которые мы рассматриваем в этом курсе, интересны для нас только как место на координатной плоскости, задаваемое парой чисел — координат. Важно, что любая пара чисел однозначно определяет точку на этой плоскости, и существование двух разных точек с одинаковыми координатами невозможно.\n\nТочки — это всего лишь один из примеров того, как мы пытаемся выразить в коде нашу предметную область, в данном случае — геометрию. Подобная абстракция используется в исходном коде графических редакторов. В финансовых приложениях используется понятие \"деньги\". Деньги можно выразить просто числом, но этого наверняка будет недостаточно, потому что помимо численного значения, для выражения денежной суммы необходимо как минимум указать ещё и валюту.\n\nМожно сказать, что в коде выше создан свой собственный, пользовательский тип данных. В отличие от примитивных типов данных, таких как числа и строки, наш новый тип является _составным_ — отсюда и понятие **составные данные**. Внутри себя он хранит набор значений, но мы оперируем ими как единым целым.\n\nРеальные прикладные приложения, по большей части, оперируют именно такими данными. Например, в Хекслете это уроки, курсы, практики, пользователи, наставники, топики в сообществе, счета и сотни других понятий. Все они гораздо более сложные, чем те же точки. В одном пользователе могут находиться десятки параметров.\n\nНевозможно заранее определить типы данных для представления всего сущего на земле. Всё, что требуется от языка — предоставить возможность определять свои собственные типы и операции над ними. И об этом мы поговорим в следующем уроке.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":3780,"name":"theory","url":"/courses/java-compound-data/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":3780,"name":"theory","url":"/courses/java-compound-data/lessons/intro/theory_unit"}],"id":1736,"slug":"intro","state":"approved","name":"Введение","course_order":10,"goal":"Знакомимся с целями и задачами курса","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"## Что это за курс?\n\nКурс \"Java: Составные данные\" никого не оставит равнодушным. Особенно это касается любителей Java, которые привыкли смотреть на всё вокруг через призму ООП. Периодически может возникать протест или же негодование с формулировками похожими на: \"в Java это проще сделать иначе!\" или \"зачем мне это всё?\". Это нормально.\n\nЭтот курс не столько о программировании на Java, сколько о программировании вообще. Он подойдёт как опытным программистам, так и тем, кто лишь недавно разобрался с примитивами и работе с простейшими методами. Попробуйте терпеливо и с открытым умом пройти курс по составным данным, и вы удивитесь насколько иначе может выглядеть программирование на Java.\n\nЗаинтересовались? Тогда ныряйте в кроличью нору)\n\n## Абстракция\n\n>Абстракция — один из главных способов борьбы со сложностью реального мира. (c) Стив Макконнелл - Совершенный код.\n\nВ этом курсе работа сосредоточится вокруг **составных данных**. Вы могли подумать, что мы будем осваивать массивы или, о ужас, писать собственные классы. И если бы мы не были Хекслетом, то всё было бы именно так. Но у нас немного другие планы на ближайший курс. Есть вещи важнее массивов или классов, и с них мы и начнём.\n\nНачиная с этого курса, мы начнём погружаться в **парадигму декларативного программирования**, без которой в современной Java никуда. Вас ждут функции как объекты первого рода, функции высшего порядка, неизменяемые данные (константы), рекурсия и другие интересные вещи. К сожалению, массивы при стандартном использовании плохо сочетаются с этими концепциями, а также позволяют срезать углы там, где не надо. Это сводит на нет весь эффект от обучения. Поэтому основой для составных данных станет так называемая **пара** — структура данных, некогда популярная во многих языках программирования. В отличие от массивов, её нельзя изменять. В любой ситуации потребуется создать новую пару на основе предыдущей, и обойти этот механизм невозможно. Подобное ограничение оставляет только один способ работы — функциональный.\n\n*Как вы увидите позже, точно так же можно и зачастую нужно работать с массивами. К тому моменту, когда мы начнём их использовать, вы уже будете готовы к такому способу работы.*\n\nОдна из самых важных тем в программировании — **абстракция**. Чем больше кодовая база, тем больше абстракций используется, либо создается в ней. Значительная часть времени разработчика тратится на моделирование предметной области и реализации её в коде, а также в её дальнейшей поддержке и развитии. Как правило, этому вопросу совсем не уделяют времени, но именно от умения моделировать зависит качество вашего кода, насколько просто будет работать с ним, понимать и модифицировать его.\n\nПредставьте, что вам необходимо автоматизировать работу отдела продаж (создать CRM). С чего вы начнёте? А начать стоит с [онтологии](https://ru.wikipedia.org/wiki/%D0%9E%D0%BD%D1%82%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0)).\n\nВ следующих курсах вы увидите, как, благодаря некоторым особым свойствам, пара может стать основой для создания более сложных структур данных, таких как списки, множества и даже деревья. Другими словами, мы заочно познакомимся с разными структурами данных и поработаем с ними.\n\nНе забывайте, что эти курсы направлены на развитие ваших навыков кодирования, и перед тем, как мы познакомимся с платформами (фреймворками, библиотеками), нам нужно разобраться с самой Java.\n\n---\n\n## Введение\n\nПо названию этого курса можно подумать, что мы будем говорить о таких типах данных в Java, как массив или объект. Это совсем не так. В этом курсе мы используем Java как язык, на котором пишем код, но сам курс относится в целом к программированию. Мы изучим такое понятие, как абстракция данных. Это общая концепция никак не связана конкретно с Java и является важным механизмом абсолютно во всех языках программирования. В отличие от предыдущих курсов мы не введём ни одной новой синтаксической конструкции. В этом и есть наше отличие. Мы учим вас мыслить, писать код и программировать,\nчто сильно отличается от того, что существует сейчас на рынке.\n\nИтак, давайте начнём.\n\n### Что такое составные данные?\n\n**Составные данные** используются в следующих случаях. Когда мы пишем программы, то чаще всего пытаемся моделировать достаточно сложные процессы и явления, протекающие часто в реальной жизни. Для этого мы можем использовать составные вычислительные объекты, которые включают в себя несколько различных частей. Это позволяет лучше моделировать все явления реальной среды, того, с чем мы взаимодействуем. И язык программирования должен предоставлять возможность создавать механизмы для создания так называемых составных данных, т.е. из разных кусков собирать отдельный вычислительный объект.\n\n### Зачем это нужно?\n\nПри работе со сложными вычислительными объектами благодаря составным данным, как я уже говорил, мы можем поднимать так называемый понятийный уровень. Т.е. работать на более высоком уровне абстракции и создавать нашу программу, и производить вычисления в терминах наших вычислительных объектов. Давайте рассмотрим это на конкретном примере, который известен всем со школы — это точки на плоскости.\n\nУ нас есть координатная плоскость, и мы можем строить на ней точки. При этом любая точка обладает двумя характеристиками — это её координаты `x` и `y`, т.е. абсцисса и ордината. В данном примере я использую то, что уже известно — простые числовые значения, и описываю две точки. Одна из них `x1/y1`, вторая `x2/y2`. После этого, вычисляю точку посередине.\n\n```java\nvar x1 = 3;\nvar y1 = 5;\n\nvar x2 = -2;\nvar y2 = 10;\n\n// Точка посередине\nvar x3 = middleX(x1, x2);\nvar y3 = middleY(y1, y2);\n```\n\nМы считаем, что мы строим отрезок между двумя этими точками и находим точку, которая лежит посередине. То, как это работает, не имеет значения. Главное, что вы можете себе это представить. Мы оперируем понятием *точка*. При этом понимаем, что она состоит из двух простых типов, т.е. двух чисел. По сути появляется такая вещь, как пара. И, в общем-то, нам удобно оперировать такими терминами.\n\nНо при написании кода, если у нас не существует понятия составные данные, то, как видите, нам приходится производить вычисления независимо. Т.е. сначала вычислить `х3`, после этого нам надо вычислить `y3`. Это происходит потому, что мы не оперируем понятием точка. У нас нет такого составного вычислительного объекта. И всё, что мы можем в наших функциях, это принимать простые параметры и возвращать точно такие же простые параметры. Получается, что одно действие технически разбивается на два. Согласитесь, это крайне неудобно. Особенно, когда ваша программа становится достаточно большой и начинает оперировать большим количеством сложных объектов.\n\nТочки, кстати говоря — это ещё достаточно простые объекты, которые включают в себя буквально 2 параметра. Представьте, что будет, когда мы начнём использовать более сложные объекты: хотя бы простые фигуры, например, квадрат. Квадрат включает в себя 4 точки — это уже 8 параметров, потому что в каждой точке 2 параметра. Работать на таком уровне абстракции, используя только примитивные типы и примитивные данные, мы просто бы не смогли. Наше мышление очень сильно бы этому сопротивлялось. Код получался бы очень громоздкий и неинтуитивный.\n\nВ идеале нам бы хотелось работать так, как показано в этой строчке:\n\n```java\n// Compound Data\nvar middlePoint = middle(point1, point2);\n```\n\nМы отдаём в функцию две точки (`point1` и `point2`), и нам возвращается точка посередине. Как она устроена и что там внутри — это отдельный вопрос. Главное, что мы оперируем этим понятием, как единым целым. Возможность строить составные объекты данных позволяет нам использовать технику, которая называется абстракция данных.\n\n**Абстракция данных** — это метод отделения частей программы, которые имеют дело с представлением объектов данных, от тех частей, где эти объекты используются.\n\nПри использовании точки в предыдущем примере это обозначает, что нас не очень волнует, как внутри устроена точка. Мы можем просто оперировать только этим понятием. Соответственно, определять то, как непосредственно внутри она выглядит, мы можем совершенно в другом месте. Это делает код модульным и даёт огромные возможности по его дальнейшей модификации и поддержке. Например, мы можем изменить представление данных на более эффективное, более удобное в данный момент и не переписывать всю программу. Нужно будет переписать только ту часть, где именно происходит определение того, как данные структурированы внутри.\n\nАбстракция данных приводит нас к такому понятию, как **барьеры абстракции**, когда мы можем строить многоуровневые слои, позволяющие изолировать разные части и разные уровни системы друг от друга. Об этом отдельный будет урок, где мы подробно поговорим, как работает абстракция данных, и как строятся барьеры абстракции.\n\nКроме этого в любом языке программирования нужен некий клей, который позволит из простых данных строить более сложные. И как мы увидим в дальнейшем, для этого даже не нужны специальные операции. Строить составные объекты можно используя только возможности функций, что ещё больше стирает разницу между функциями и данными. Это можно было уже заметить, потому что функции определяются точно так же, как данные. Функции являются полноправными данными, так называемыми объектами первого рода. Этому будет посвящён целый отдельный урок.\n\n### Графические примитивы\n\nНа протяжении всего курса мы будем строить графические примитивы и небольшую библиотеку для работы с примитивами на плоскости. Начнём с точек, поработаем с кругами, научимся делать различные отрезки и фигуры.\n\n### Рациональные числа\n\nКроме этого попробуем создать простую библиотеку для работы с рациональными числами. Это числа, у которых есть числитель и знаменатель. Определение, конечно, не строго математическое, но вы на интуитивном уровне все с ними знакомы. Рациональное число — достаточно простой, но составной объект, который включает в себя пару чисел.\n\n### Преимущества Hexlet\n\n- Осмысление\n- СИКП\n- Не используем существующие структуры данных\n- Функции высшего порядка\n- Неизменяемость (Функциональный стиль)\n\nПо опыту предыдущих курсов очень часто у людей возникают вопросы: \"А почему именно так?\", \"Как мне это поможет в практике?\" и \"Я хочу быстрее начать писать продакшн-код во фреймворках\". Да, мы не просто учим синтаксису. Мы хотим изменить ваше мышление, хотим сделать его правильным, чтобы у вас развилось критическое мышление, чтобы вы понимали причинно-следственные связи и, в первую очередь, мыслили не синтаксисом языка, а смыслом того, что вы делаете с точки зрения построения абстракций. Это позволит писать вам модульные программы, которые легко читать, поддерживать, развивать, и они будут выполнять то, что нам нужно. Это наша основная идея, то, что хочет дать и несёт Хекслет. Возможно, вы уже заметили это по каким-то вещам, которые мы рассказываем или делаем.\n\nНаш курс во многом построен на так называемом СИКПе — курсе, который вёлся и ведётся до сих пор в большом количестве университетов мира. Расшифровывается, как структура и интерпретация компьютерных программ. Курс был придуман в Массачусетском технологическом институте (MIT). Это университет номер один в мире IT-технологий, который выпускал и выпускает лучших специалистов в этой области. СИКП — достаточно большой, сложный, но крайне важный курс. По нему выпущена книга, которая уже десятки лет считается книгой номер один среди обучающей литературы по программированию. В любом случае, всем её рекомендуем. При этом здесь мы её активно используем, потому что она дала нам много материала и пищи для размышлений. Книга была выпущена в 1985 году, а сам курс вёлся ещё раньше (с начала 80-х). Но, как вы увидите в дальнейшем, несмотря на то, что меняются технологии, меняются фреймворки, меняется всё вокруг (Java меняется особенно сильно, несмотря на принцип обратной совместимости), базовые вещи, так же как во многом и математика, в общем-то не меняется и не поменяется. Не гонитесь за новым модным, изучайте то, на чём всё это базируется.\n\nКурс не использует существующие структуры данных. Я сказал об этом в самом начале. Здесь не будет ничего про массивы, объекты или какие-то другие возможные способы комбинирования данных. Это отвлечёт нас от основной идеи. От понимания сути вопроса. Наши функции в этом курсе приобретут совершенно новый оттенок и заиграют новыми красками. Мы познакомимся с функциями высшего порядка и увидим, что функции — это точно такие же данные, которые можно передавать в другие функции, возвращать из функций и делать с ними совершенные чудеса.\n\nВ курсе мы будем работать с неизменяемыми данными, использовать функциональный стиль программирования. Этот достаточно важный аспект позволяет нам сосредоточится только на самом вопросе, который разбирается в курсе, и оставить вопрос изменения состояния и связанных с этим проблем в стороне. В Основах программирования мы уже немного говорили о том, какие проблемы привносят переменные и возможность изменять состояние. В дальнейшем будет отдельный большой курс, посвящённый работе с состояниями и всеми особенностями, которые привносит возможность использовать переменные.\n\nДа, несмотря на то, что курс адаптирован под Java, мы сознательно пользуемся термином функция, а не метод. Для этого есть важные причины, но сейчас мы не будем углубляться в этот вопрос.\n"},"id":211,"slug":"java-compound-data","challenges_count":3,"name":"Java: Составные данные","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы изучите идею составных данных. Вы узнаете больше о концепциях создания сложных типов данных из простых и о парадигме декларативного программирования. В итоге научитесь создавать абстракции и изолировать разные части программы. Знания из этого курса помогают программистам моделировать необходимую предметную область, писать более читаемый и модульный код.","kind":"additional","updated_at":"2026-01-20T11:46:13.822Z","language":"java","duration_cache":29880,"skills":["Создавать код, который легко читать и понимать, что он делает","Научиться создавать удобные абстракции и скрывать внутреннюю реализацию данных","Определять границу между слоями приложения так, чтобы поддерживать высокий уровень модульности (независимости разных частей) кода"],"keywords":["моделирование данных","барьеры абстракции","замыкание"],"lessons_count":7,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NzQ3OCwicHVyIjoiYmxvYl9pZCJ9fQ==--cd0f377ee69a4d823cf77e908feb5377e27c16e1/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJwbmciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--6067466c2912ca31a17eddee04b8cf2a38c6ad17/image.png"},"recommendedLandings":[{"stack":{"id":36,"slug":"java-sicp","title":"СИКП на Java","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4100,"duration_in_months":1},"id":60,"slug":"java-sicp","title":"СИКП на Java","subtitle":"Навык понимать программы на фундаментальном уровне, уверенно проходить собеседования и решать сложные задачи","subtitle_for_lists":"Изучите фундаментальные принципы программирования на Java","locale":"ru","current":true,"duration_in_months_text":"1 месяц","stack_slug":"java-sicp","price_text":"от 3 900 ₽","duration_text":"1 месяц","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDAxNiwicHVyIjoiYmxvYl9pZCJ9fQ==--eb66b9b5e26fafa32844ce0f4522c3ed84544040/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/java-compound-data/lessons/points/theory_unit","version":"0b0c6d4ebbd40fd58630a0dd89cc25544ccdf24e","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Java: Составные данные</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Точки</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Точки","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"Java: Составные данные"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>В этом уроке мы начнём проектирование библиотек для работы с графическими примитивами (точками, отрезками), а также фигурами (кругами, прямоугольниками). Естественно, проще всего начать с основы — библиотеки для работы с точками. Для этого давайте вспомним координатную плоскость и то, как определяются на ней точки.</p>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NzQ3OSwicHVyIjoiYmxvYl9pZCJ9fQ==--6da8f297a89cd12884c408f078aa6f6d7289fdab/plane.png" alt="coordinate plane" loading="lazy"/></p>
<p>В этом примере мы видим плоскость, на которой расположены две оси (ось абсцисс X и ось ординат Y) и точки. При этом обычно точка описывается двумя параметрами: первым указывается координата x (абсцисса), вторым — координата y (ордината). Например, точка D описывается как (2, 4), точка C — (1, 0) и так далее. Координаты могут быть отрицательными. Оси делят плоскость на четыре части, которые называются квадрантами. На рисунке точка D лежит в первом квадранте, точки A, G, E — во втором, в третьем и в четвёртом квадрантах соответственно.</p>
<p>Когда мы говорим о точках, мы на самом деле имеем в виду так называемые <strong>абстрактные данные</strong>. Это означает, что мы не делаем никаких предположений (кроме самых необходимых) для того, чтобы рассуждать в терминах этих данных (в нашем случае — точек). И действительно, вспомним, например, любой раздел школьной математики: когда мы оперируем какими-то понятиями, мы вообще не думаем о том, что они должны быть как-то представлены, мы мыслим в них на абстрактном уровне. Это позволяет нам строить законы, делать выводы и предположения о том, как будут вести себя данные, как они друг с другом комбинируются. При этом нам не нужна никакая реализация, нам не нужен ни язык программирования, ни компьютер. Более того, нам вообще необязательно графически изображать эти данные — мы можем представлять это в голове и при этом делать выводы, имеющие практическое значение. При этом конкретное представление абстрактных данных (так называемые <strong>конкретные данные</strong>) определяется условиями прикладного использования, например, особенностями разных языков программирования. Зачастую библиотеки, которые снаружи выглядят одинаково и оперируют одними и теми же понятиями, внутри устроены совершенно по-разному. Естественно, при манипуляциях с такими библиотеками необходимо оставаться в рамках понятийного аппарата тех абстрактных данных, с которыми мы работаем. "Клеем" между абстрактными данными и конкретными данными выступает так называемый интерфейс. Интерфейс — это набор функций, обычно разделяемых на конструкторы и селекторы. Конструкторы позволяют из набора данных строить составной объект, а с помощью селекторов из составного объекта извлекают его части. В нашем случае, если говорить о точках, селекторы позволяют извлекать координаты x и y. Вот как выглядит интерфейс нашей библиотеки:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">var x = 5;
var y = -7;
// Конструктор
var point = makePoint(x, y);
// Селекторы
getX(point);
getY(point);
pointToString(point); // (5, -7)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>В коде выше видно, что мы определили функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">makePoint</code>, которая принимает на вход два числа (<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">x</code> и <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">y</code>) и возвращает точку с такими координатами. Кроме этого, есть два основных селектора <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">getX</code> и <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">getY</code>, которые возвращают соответствующие координаты из точки.
При проектировании библиотеки мы используем стратегию "мечтать не вредно", потому что ещё не знаем, как она будет устроена внутри. Это, по большому счету, не имеет значения, потому что мы пользуемся абстрактными данными и оперируем терминами нашей предметной области, а не конкретной реализации. Поэтому мы можем сосредоточиться на сути, отложив принятие решения о деталях.</p>
<p>Кроме основных селекторов, нами определена функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">pointToString</code>, которая возвращает текстовое представление точки и может быть использована для отладки.</p>
<p>Давайте посмотрим, какие манипуляции можно производить над точками, имея базовый интерфейс:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">public static Pair symmetricalXPoint(Pair point) {
return makePoint(getX(point), -getY(point));
}
var point = makePoint(10, 10);
pointToString(symmetricalXPoint(point)); // (10, -10)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>В примере выше определена функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">symmetricalXPoint</code>, которая возвращает точку, симметричную заданной относительно оси X. Например, для точки c координатами (10, 10) функция вернёт точку с координатами (10, -10). Функция устроена следующим образом: с помощью селекторов мы извлекаем координаты, инвертируем координату Y и создаём новую точку с помощью конструктора <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">makePoint</code>.
Большинство операций с точками состоят в том, чтобы извлечь координаты, произвести с ними необходимые изменения и создать новые точки. С помощью этого нехитрого алгоритма можно реализовать массу других функций, например:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">var point1 = makePoint(3, 4);
var point2 = makePoint(0, 0);
quadrant(point1); // 1
distance(point1, point2); // 5</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">quadrant</code> определяет квадрант, в котором расположена точка, а функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">distance</code> вычисляет расстояние между заданными точками. При этом мы оперируем понятием "точки", а обращение к составным частям этих точек происходит внутри и скрыто от наших глаз.</p>
<p>Подведём итог:</p>
<p>Абстрактными данными у нас является точка, которая характеризуется двумя значениями (координатами) <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">x</code> и <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">y</code>.</p>
<p>Конкретные данные: на текущий момент мы не знаем, как реализованы точки (что не мешает нам пользоваться библиотекой в рамках нашей предметной области).</p>
<p>Интерфейс:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">// Конструктор
makePoint(x, y);
// Селекторы
getX(point);
getY(point);</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<hr/>
<h2 id="heading-2-1">Внутренняя реализация</h2>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">var point = makePoint(4, 5);
System.out.println(getX(point));
System.out.println(getY(point));
System.out.println(point); // => 'don't do it'
System.out.println(pointToString(point));
System.out.println(quadrant(point));</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Если вас мучает вопрос, что находится внутри переменной <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">point</code>, скажу сразу — мы ответим на него в следующих уроках. Для работы с точками не нужно знать их устройство. Когда мы решаем геометрические задачи, то легко оперируем понятием отрезка, круга и других фигур. С помощью формул мы можем находить площадь, рассчитывать расстояние между точками, проводить касательные и делать многое другое.</p>
<p>Каждое из перечисленных выше понятий абстрактно, не имеет прямого воплощения в реальной жизни и может применяться как к описанию детских игрушек (круглый мяч), так и египетских пирамид. Причём в каждом конкретном случае нас будут интересовать разные детали. В одних случаях важны только размеры, в других — расположение относительно некоей системы координат, в третьих — цвет фигуры.</p>
<p>В некоторых задачах при работе с точками могут отсутствовать координаты, но имеются цвет или скорость мерцания. Тогда набор функций для работы с подобными точками будет кардинально отличаться от нашего набора.</p>
<p>Точки, которые мы рассматриваем в этом курсе, интересны для нас только как место на координатной плоскости, задаваемое парой чисел — координат. Важно, что любая пара чисел однозначно определяет точку на этой плоскости, и существование двух разных точек с одинаковыми координатами невозможно.</p>
<p>Точки — это всего лишь один из примеров того, как мы пытаемся выразить в коде нашу предметную область, в данном случае — геометрию. Подобная абстракция используется в исходном коде графических редакторов. В финансовых приложениях используется понятие "деньги". Деньги можно выразить просто числом, но этого наверняка будет недостаточно, потому что помимо численного значения, для выражения денежной суммы необходимо как минимум указать ещё и валюту.</p>
<p>Можно сказать, что в коде выше создан свой собственный, пользовательский тип данных. В отличие от примитивных типов данных, таких как числа и строки, наш новый тип является <em>составным</em> — отсюда и понятие <strong>составные данные</strong>. Внутри себя он хранит набор значений, но мы оперируем ими как единым целым.</p>
<p>Реальные прикладные приложения, по большей части, оперируют именно такими данными. Например, в Хекслете это уроки, курсы, практики, пользователи, наставники, топики в сообществе, счета и сотни других понятий. Все они гораздо более сложные, чем те же точки. В одном пользователе могут находиться десятки параметров.</p>
<p>Невозможно заранее определить типы данных для представления всего сущего на земле. Всё, что требуется от языка — предоставить возможность определять свои собственные типы и операции над ними. И об этом мы поговорим в следующем уроке.</p></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/java-sicp?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">1 месяц</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">СИКП на Java</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите фундаментальные принципы программирования на Java</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDAxNiwicHVyIjoiYmxvYl9pZCJ9fQ==--eb66b9b5e26fafa32844ce0f4522c3ed84544040/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png" alt="СИКП на Java" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/java-compound-data/lessons/points/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 7</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/java-compound-data/lessons/points/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-CdBlNCiQ.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-nkZBEvfU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>