<!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 18:42:27 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="Y1ZHTc3uGdyFfSeVZiclsg7m3lx5YpwvKnT_3IZzqrKMh4x6P5C0vDM-Aw1qKNXFzu_z9nFVYo2XlGWI1HRN3A";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>Плюсы и минусы разных способов организации абстракций | PHP: Введение в ООП</title>
<meta name="description" content="Плюсы и минусы разных способов организации абстракций / PHP: Введение в ООП: Выясняем, чем хороша и чем плоха инкапсуляция">
<link rel="canonical" href="https://ru.hexlet.io/courses/php-introduction-to-oop/lessons/differences/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Плюсы и минусы разных способов организации абстракций">
<meta property="og:title" content="PHP: Введение в ООП">
<meta property="og:description" content="Плюсы и минусы разных способов организации абстракций / PHP: Введение в ООП: Выясняем, чем хороша и чем плоха инкапсуляция">
<meta property="og:url" content="https://ru.hexlet.io/courses/php-introduction-to-oop/lessons/differences/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="2X4IHBPPtFTMmAqvHy5u1g6Ll8o4-sRqKOkJ7I7ePc42r8Mr4bEZNHrbLjcTIZ6hzoK6YDDNOsiVCZO43NnaoA" />
<script src="/vite/assets/inertia-DfXos102.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-BrRXra1y.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-cb8xch9l.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/eyJfcmFpbHMiOnsiZGF0YSI6Mzk5MiwicHVyIjoiYmxvYl9pZCJ9fQ==--e9d0f30948ea766a7e6bc3e3d56c192344d45fb8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programming-cuate%20(1).png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1MSwicHVyIjoiYmxvYl9pZCJ9fQ==--e5793a1818ff43d73135cc7ed88c1998d7650470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-bro.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxNSwicHVyIjoiYmxvYl9pZCJ9fQ==--ee06f514098550b5ac7eebce6227b1543399a37c/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-26T18:42:27.678Z","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":"LA1EanvDA0TC-y8UevlS252p8lguTm2ODzJHGT9CqSzD3I9dib2uJHS4C4x29qKsXaDf8iZ5kyyy0t1NbUVOQg","topics":[{"id":51347,"title":"В недостатках пункт 1 повторяется два раза. Номера пунктов по ctrl+enter не выделяются, поэтому пишу здесь.","plain_title":"В недостатках пункт 1 повторяется два раза. Номера пунктов по ctrl+enter не выделяются, поэтому пишу здесь. ","creator":{"public_name":"Алексей Швецов","id":199233,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":109774,"body":"Приветствую, Алексей!\n\nСпасибо! Fixed!","topic_id":51347}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Плюсы и минусы разных способов организации абстракций","entity_url":null,"active":true}},{"id":22014,"title":"Спасибо, курс отличный!\n\nОтдельное спасибо за Николая Рыжикова и Рича Хикки. Оба крутые дядьки.","plain_title":"Спасибо, курс отличный! Отдельное спасибо за Николая Рыжикова и Рича Хикки. Оба крутые дядьки. ","creator":{"public_name":"Константин Проскурня","id":178216,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":46855,"body":"Пожалуйста)","topic_id":22014}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Плюсы и минусы разных способов организации абстракций","entity_url":null,"active":true}},{"id":16575,"title":"ООП в современном мире, воспринимается большим числом программистов (особенно начинающими) как серебрянная (**серебряная**) пуля, средство от всех болезней. Учитывая ( **,** ) что в PHP это основной способ строить абстракции, у вас может сложиться такое же впечатление. Но это не так. Во-первых ( **,** ) под ООП понимают две абсолютно разные концепции. Та ( **,** ) которую мы обсуждаем, является мейнстримом и встроена во многие языки настолько глубоко, что писать в другом стиле либо невозможно ( **,** ) либо очень тяжело. Но есть и другая, созданная Аланом Кеем. Что интересно, именно Алан является создателем термина ООП, но его ООП не имеет почти ничего общего с тем ( **,** ) что сейчас называется ООП.\n\nВо-вторых ( **,** ) существуют другие способы получить поведение похожее на то ( **,** ) которое вы будете наблюдать в ООП коде. Более того, многие из них значительно мощнее в возможностях (и некоторые появились задолго до ООП языков). Например ( **,** ) мультиметоды в языке Clojure дают большую свободу (мультидиспетчеризацию) и позволяют строить полиморфизм на специализированных функциях.\n\nСейчас лишь скажу, что если мы вызываем функцию, то это всегда некоторая конкретная функция ( **,** ) импортированная из конкретного неймспейса.\n\nТак как любая сущность представляется этим массивом, то можно по ошибке вызывать функцию ( **,** ) предназначенную для одной абстракции, например точки, на другой абстракции, например сегменте. \n\nВ итоге программа продолжит работать некорректно, вместо того чтобы заврешитсья (**завершится**) с ошибкой.\n\nВызываемый метод всегда принадлежит тому объекту ( **,** ) на котором он вызывается. \n\nЕсли метода нет ( **,** ) то будет ошибка, если есть, то он отработает так ( **,** ) как и должен отработать.\n\nНо это преимущество является преимуществом только при сравнении с абстракциями ( **,** ) построенными на общих структурах данных (и в динамических языках) ( **,** ) такими как ассоциативные массивы. \n\nЭто преимущество немного неожиданно. (**неожиданное** ,т.к. неожиданно - это наречие, отвечает на вопрос \"как\" и применяется только с глаголами)\n\nВ некоторых современных языках поддерживается Unified Function Call (nim, d, rust) (**кривая ссылка на Unified Function Call**)\n\n_Комментарий к коду на языке Nim:_\nОпределяется функция ( **,** ) принимающая на вход два вектора и возвращающая новый веткор (**вектор**),\nполученный сложением исходных векторов\n\nСправедливости ради, (**здесь запятой не надо**) скажу ( **,** ) что ( **,** ) несмотря на это, их всегда можно обойти (например ( **,** ) используя Reflection API).\n\nСуществуют языки, в которых эту проблему пытаются решать ( **,** ) позволяя \"дописывать\" определение класса по ходу работы программы, например ruby. В js тоже самое достигается за счет механизма прототипов. Языки ( **,** ) в которых функции и данные разделены - не имеют подобного недостатка ( **,** ) и код на них пишется, как ни странно, легче и проще (хотя местами многословнее). Сюда же можно отнести проблему ( **,** ) называемую антипаттерном (плохой реализацией) god object.\n\nОб этом я расскажу в соответсвующем (**соответствующем**) курсе, когда мы разберем суть наследования как отношения между типами и ограничениями ( **,** ) без которых наследование невозможно.\n\nСуществует миф о том ( **,** ) что программы ( **,** ) написанные в ООП стиле (на самом деле, имеется ввиду та самая модель ООП которая используется в языках типа PHP или Java) при больших размерах делает код компактнее.\n\nНесмотря на кажущуюся абсурдность, подобная проблема реально (**реальна**) и проявляется очень часто.\n\nДумаю ( **,** ) что влияние этого пункта вы уже ощутили на себе, хотя мы только начали. \n\nВ итоге одно и тоже (**одно и то же**) поведение можно реализовать десятком разных способов. Приходится знать тысячи ньюансов (**нюансов**) и постоянно решать споры о том ( **,** ) какой подход лучше.\n\n(**В разделе \"Недостатки\" пункты идут 1 .. 1 .. 2, а не 1, 2, 3**)\n\n\n","plain_title":"ООП в современном мире, воспринимается большим числом программистов (особенно начинающими) как серебрянная (серебряная) пуля, средство от всех болезней. Учитывая ( , ) что в PHP это основной способ строить абстракции, у вас может сложиться такое же впечатление. Но это не так. Во-первых ( , ) под ООП понимают две абсолютно разные концепции. Та ( , ) которую мы обсуждаем, является мейнстримом и встроена во многие языки настолько глубоко, что писать в другом стиле либо невозможно ( , ) либо очень тяжело. Но есть и другая, созданная Аланом Кеем. Что интересно, именно Алан является создателем термина ООП, но его ООП не имеет почти ничего общего с тем ( , ) что сейчас называется ООП. Во-вторых ( , ) существуют другие способы получить поведение похожее на то ( , ) которое вы будете наблюдать в ООП коде. Более того, многие из них значительно мощнее в возможностях (и некоторые появились задолго до ООП языков). Например ( , ) мультиметоды в языке Clojure дают большую свободу (мультидиспетчеризацию) и позволяют строить полиморфизм на специализированных функциях. Сейчас лишь скажу, что если мы вызываем функцию, то это всегда некоторая конкретная функция ( , ) импортированная из конкретного неймспейса. Так как любая сущность представляется этим массивом, то можно по ошибке вызывать функцию ( , ) предназначенную для одной абстракции, например точки, на другой абстракции, например сегменте. В итоге программа продолжит работать некорректно, вместо того чтобы заврешитсья (завершится) с ошибкой. Вызываемый метод всегда принадлежит тому объекту ( , ) на котором он вызывается. Если метода нет ( , ) то будет ошибка, если есть, то он отработает так ( , ) как и должен отработать. Но это преимущество является преимуществом только при сравнении с абстракциями ( , ) построенными на общих структурах данных (и в динамических языках) ( , ) такими как ассоциативные массивы. Это преимущество немного неожиданно. (неожиданное ,т.к. неожиданно - это наречие, отвечает на вопрос \"как\" и применяется только с глаголами) В некоторых современных языках поддерживается Unified Function Call (nim, d, rust) (кривая ссылка на Unified Function Call) Комментарий к коду на языке Nim: Определяется функция ( , ) принимающая на вход два вектора и возвращающая новый веткор (вектор), полученный сложением исходных векторов Справедливости ради, (здесь запятой не надо) скажу ( , ) что ( , ) несмотря на это, их всегда можно обойти (например ( , ) используя Reflection API). Существуют языки, в которых эту проблему пытаются решать ( , ) позволяя \"дописывать\" определение класса по ходу работы программы, например ruby. В js тоже самое достигается за счет механизма прототипов. Языки ( , ) в которых функции и данные разделены - не имеют подобного недостатка ( , ) и код на них пишется, как ни странно, легче и проще (хотя местами многословнее). Сюда же можно отнести проблему ( , ) называемую антипаттерном (плохой реализацией) god object. Об этом я расскажу в соответсвующем (соответствующем) курсе, когда мы разберем суть наследования как отношения между типами и ограничениями ( , ) без которых наследование невозможно. Существует миф о том ( , ) что программы ( , ) написанные в ООП стиле (на самом деле, имеется ввиду та самая модель ООП которая используется в языках типа PHP или Java) при больших размерах делает код компактнее. Несмотря на кажущуюся абсурдность, подобная проблема реально (реальна) и проявляется очень часто. Думаю ( , ) что влияние этого пункта вы уже ощутили на себе, хотя мы только начали. В итоге одно и тоже (одно и то же) поведение можно реализовать десятком разных способов. Приходится знать тысячи ньюансов (нюансов) и постоянно решать споры о том ( , ) какой подход лучше. (В разделе \"Недостатки\" пункты идут 1 .. 1 .. 2, а не 1, 2, 3) ","creator":{"public_name":"Артем Остащенко","id":129599,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":35098,"body":"Опечатки исправлены, спасибо!","topic_id":16575}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Плюсы и минусы разных способов организации абстракций","entity_url":null,"active":true}},{"id":28878,"title":"**Начало цитаты** В общем и целом, чем больше разных по структуре языков и парадигм вы знаете, тем лучше понимаете, что происходит. **Конец цитаты**\n\nя слышал еще такую точку зрения (и неоднократно видел в интернете), что если изучаешь программирование, то нужно концентрироваться на одном языке - чтобы не распыляться и быстрее стать спецом. не знаю какой из этих подходов правильный","plain_title":"Начало цитаты В общем и целом, чем больше разных по структуре языков и парадигм вы знаете, тем лучше понимаете, что происходит. Конец цитаты я слышал еще такую точку зрения (и неоднократно видел в интернете), что если изучаешь программирование, то нужно концентрироваться на одном языке - чтобы не распыляться и быстрее стать спецом. не знаю какой из этих подходов правильный ","creator":{"public_name":"Evgenij Goldin","id":184359,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":62346,"body":"На разных этапах обучения нужны разные подходы. Если человек только только знакомиться с базовыми понятиями, типа переменных, то да, надо дойти до некоторого уровня только на одном языке (этот уровень есть на code-basics.ru). А вот дальше, лучше смотреть \"а как еще\", иначе начнется деформация.","topic_id":28878}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Плюсы и минусы разных способов организации абстракций","entity_url":null,"active":true}},{"id":33572,"title":"Так все-таки про что ООП в превую очередь? Про абстрагирование с помощью объектов или про мышление типами при моделировании предметной области?","plain_title":"Так все-таки про что ООП в превую очередь? Про абстрагирование с помощью объектов или про мышление типами при моделировании предметной области? ","creator":{"public_name":"Дмитрий Плотников","id":168368,"is_tutor":false},"comments":[{"creator":{"public_name":"Дмитрий Плотников","id":168368,"is_tutor":false},"id":73365,"body":"Насколько я понял, абстракция над данными всегда строится исходя из того, что у нас есть какой-то тип, с экземплярами которого мы хотим манипулировать различным образом. При чем тип может быть не выражен явно в коде, а просто определен набором операций, доступных с этим типом. И задача ООП как раз в том что-бы описывать типы явно, использую такие конструкции как класс. Все верно?","topic_id":33572},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":73380,"body":"> И задача ООП как раз в том что-бы описывать типы явно, использую такие конструкции как класс\n\nНет. Это прекрасно и без ооп делается. Механизмы для создания абстракций есть практически во всех высокоуровневых языках, но для этого не нужны классы. В ооп просто свой путь их создавать. В других языках свой.","topic_id":33572},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":73207,"body":"Можно сказать так, чтобы программа выглядела как ооп программа и писалась в таком стиле, достаточно добавить инкапсуляцию (в js легко писать в разных стилях, там это легко проверяется). А дальше, все те вещи которые были и до ооп, начинают выглядеть именно так, как все уже привыкли (в ооп мире).\n\nА вот чтобы дальше разобраться в этом вопросе, нужно пройти оставшиеся 3 курса, где разбирается ооп дизайн, полиморфизм и наследование. Только после этого можно будет ответить предметно.","topic_id":33572}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Плюсы и минусы разных способов организации абстракций","entity_url":null,"active":true}},{"id":16729,"title":"Несогласованное предложение: \"Работая с абстракцией, которая основана на ассоциативном массиве, таит в себе один сюрприз.\"","plain_title":"Несогласованное предложение: \"Работая с абстракцией, которая основана на ассоциативном массиве, таит в себе один сюрприз.\" ","creator":{"public_name":"Сергей Яковлев","id":28094,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":35454,"body":"Поправил, спасибо!","topic_id":16729}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Плюсы и минусы разных способов организации абстракций","entity_url":null,"active":true}},{"id":16270,"title":"Спасибо за новый курс по ООП. Первый шел очень тяжело, было много вопросов не понимания. Читала книгу, что бы его пройти. И даже после этого было много вопросов.\nНо этот курс, как раз для тех, кто еще ни разу не видел ооп, классы и не умеет с ними работать. Спасибо. Многие вопросы нашли свои ответы тут. \nА будет продолжение, что бы разобрать и поработать с инкапсуляцией, наследованием и полиморфизмом? \nИ какие еще курсы будут обновляться? ","plain_title":"Спасибо за новый курс по ООП. Первый шел очень тяжело, было много вопросов не понимания. Читала книгу, что бы его пройти. И даже после этого было много вопросов. Но этот курс, как раз для тех, кто еще ни разу не видел ооп, классы и не умеет с ними работать. Спасибо. Многие вопросы нашли свои ответы тут. А будет продолжение, что бы разобрать и поработать с инкапсуляцией, наследованием и полиморфизмом? И какие еще курсы будут обновляться? ","creator":{"public_name":"Юлия Стрелкова","id":164543,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":34427,"body":"Спасибо! Да будет, и этот курс еще в процессе доработки (кстати если у вас есть вопросы по контенту, то задавайте).\n\nПочти все что сейчас есть в процессии станет не актуально. В ближайшее время выйдет курс \"объектно-ориентированный дизайн\", а за ним пойдет веб-разработка.","topic_id":16270}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Плюсы и минусы разных способов организации абстракций","entity_url":null,"active":true}},{"id":16596,"title":"В js тоже (**то же**) самое достигается за счет механизма прототипов. Языки, в которых функции и данные разделены - не имеют подобного недостатка ( **,** ) и код на них пишется, как ни странно, легче и проще (хотя местами многословнее). \n\nПредложение: 3.\t_Это преимущество немного неожиданно._ \nЯ всё таки не уверен, что преимущество неожиданно, а не неожиданное. Тут лучше проконсультироваться со специалистами, но насколько я знаю, наречие отвечает на вопрос КАК и применяется с глаголами (например, выскочить неожиданно из-за угла). Но преимущество неожиданно ??? С существительными могут употребляться только прилагательные, отвечающие на вопрос КАКОЕ. Поэтому преимущество - неожиданное. ","plain_title":"В js тоже (то же) самое достигается за счет механизма прототипов. Языки, в которых функции и данные разделены - не имеют подобного недостатка ( , ) и код на них пишется, как ни странно, легче и проще (хотя местами многословнее). Предложение: 3. Это преимущество немного неожиданно. Я всё таки не уверен, что преимущество неожиданно, а не неожиданное. Тут лучше проконсультироваться со специалистами, но насколько я знаю, наречие отвечает на вопрос КАК и применяется с глаголами (например, выскочить неожиданно из-за угла). Но преимущество неожиданно ??? С существительными могут употребляться только прилагательные, отвечающие на вопрос КАКОЕ. Поэтому преимущество - неожиданное. ","creator":{"public_name":"Артем Остащенко","id":129599,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":35191,"body":"Спасибо, Артём, поправил опечатки. Тексты новых курсов ещё только будут проходить редактуру (мы их опубликовали, чтобы пользователи как можно раньше могли начать обучение), но ваша обратная связь очень здорово помогает!\n\n> С существительными могут употребляться только прилагательные, отвечающие на вопрос КАКОЕ. \n\nЭто [краткое прилагательное](https://kartaslov.ru/%D0%BF%D1%80%D0%BE%D1%81%D0%BA%D0%BB%D0%BE%D0%BD%D1%8F%D1%82%D1%8C-%D0%BF%D1%80%D0%B8%D0%BB%D0%B0%D0%B3%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5/%D0%BD%D0%B5%D0%BE%D0%B6%D0%B8%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9). Но лучше на этом сильно не зацикливаться :)","topic_id":16596}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Плюсы и минусы разных способов организации абстракций","entity_url":null,"active":true}},{"id":107767,"title":"Видео из дополнительных материалов к уроку было удалено с ютуба (изоморфизм Рича Хикки). Можно где-нибудь что-то подобное почитать или посмотреть?","plain_title":"Видео из дополнительных материалов к уроку было удалено с ютуба (изоморфизм Рича Хикки). Можно где-нибудь что-то подобное почитать или посмотреть? ","creator":{"public_name":"Анастасия Маслова","id":676123,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikolai Gagarinov","id":104929,"is_tutor":true},"id":201846,"body":"Добрый день.\n\nК сожалению пока нет( Не могу найти нигде это видео. Разве что текстовую расшифровку (но она не полная) [здесь](https://inexsu.wordpress.com/2024/12/12/николай-рыжиков-полный-стэк-на-одном/)","topic_id":107767}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Плюсы и минусы разных способов организации абстракций","entity_url":null,"active":true}}],"lesson":{"exercise":null,"units":[{"id":2336,"name":"theory","url":"/courses/php-introduction-to-oop/lessons/differences/theory_unit"},{"id":2577,"name":"quiz","url":"/courses/php-introduction-to-oop/lessons/differences/quiz_unit"}],"links":[],"ordered_units":[{"id":2336,"name":"theory","url":"/courses/php-introduction-to-oop/lessons/differences/theory_unit"},{"id":2577,"name":"quiz","url":"/courses/php-introduction-to-oop/lessons/differences/quiz_unit"}],"id":1113,"slug":"differences","state":"approved","name":"Плюсы и минусы разных способов организации абстракций","course_order":900,"goal":"Выясняем, чем хороша и чем плоха инкапсуляция","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Теперь, после того, как мы немного поработали с объектами, давайте попытаемся ответить на вопрос: \"какую такую задачу они решают, которую не решают абстракции на основе обычных функций + ассоциативный массив как структура\"?\n\n_Изложенные ниже тезисы могут показаться вам совсем чуждыми и непонятными в силу отсутствия опыта. В этом нет ничего страшного, главное — увидеть направления, а отработкой мы займёмся в следующих курсах._\n\nПеред тем как начать уходить глубже в тему объектов, хочу вас предостеречь. ООП в современном мире воспринимается большим числом программистов (особенно начинающими), как серебряная пуля, как средство от всех болезней. Учитывая, что в PHP это основной способ строить абстракции, у вас может сложиться такое же впечатление. Но это не так. Во-первых, под ООП понимают две абсолютно разные концепции. Та, которую мы обсуждаем, является мейнстримом, и встроена во многие языки настолько глубоко, что писать в другом стиле либо невозможно, либо очень тяжело. Но есть и другая, созданная _Аланом Кеем_ (Alan Key). Что интересно, именно Алан является создателем термина ООП, но его ООП не имеет почти ничего общего с тем, что сейчас называется ООП.\n\n> ООП для меня это сообщения, локальное удержание и защита, скрытие состояния и позднее связывание всего. Это можно сделать в Smalltalk и в LISP. Алан Кей\n\nВо-вторых, существуют другие способы получить поведение, похожее на то, которое вы будете наблюдать в ООП-коде. Более того, многие из них значительно мощнее в возможностях (и некоторые появились задолго до ООП-языков). Например, мультиметоды в языке Clojure дают большую свободу (мультидиспетчеризацию) и позволяют строить полиморфизм на специализированных функциях.\n\nВ общем и целом, чем больше разных по структуре языков и парадигм вы знаете, тем лучше понимаете, что происходит. Рекомендую: Clojure, Haskell, Elixir, Kotlin и JavaScript (последний стандарт).\n\n## Преимущества\n\n1. Пожалуй, основное преимущество связано с **полиморфизмом подтипов**. Подробно эта тема освещается позже. Сейчас лишь скажу, что если мы вызываем функцию, то это всегда некоторая конкретная функция, импортированная из конкретного пространства имён. А вот если мы вызываем метод, то появляются варианты. Когда интерпретатор доходит до кода с вызовом метода `$obj->methodCall()`, он не может сразу сказать, где определён данный метод, потому что ответ на этот вопрос зависит от того, какой тип у `$obj`. Отсюда следует, что, если разные объекты содержат методы с одинаковым именем (и сигнатурой), то их можно прозрачно (для вызывающего кода) подменять. На практике такая возможность местами упрощает код (становится меньше условных конструкций), но главное — делает его расширение проще.\n\n2. Работа с абстракцией, основанной на ассоциативном массиве, таит в себе один сюрприз. Так как любая сущность представляется этим массивом, то можно по ошибке вызывать функцию, предназначенную для одной абстракции (например, точки), на другой абстракции (например, отрезка). Что при этом произойдёт — непонятно, и зависит от того, насколько удачно совпали структуры. И если такое произошло, то функция внезапно может отработать без ошибок и даже что-то вернуть. В итоге программа продолжит работать некорректно, вместо того, чтобы завершиться с ошибкой. Инкапсуляция исключает подобную ситуацию. Вызываемый метод всегда принадлежит тому объекту, на котором он вызывается. Если метода нет, то будет ошибка, если есть — то он отработает так, как и должен отработать. Но это преимущество является преимуществом только при сравнении с абстракциями, построенными на общих структурах данных (и в динамических языках), такими, как ассоциативные массивы. В языках с развитой системой типов (но без ООП), например, в Haskell, подобной проблемы также нет.\n\n ```php\n <?php\n\n $segment = makeSegment(makePoint(1, 3), makePoint(10, 11));\n // Функция отработает, хотя в неё передали отрезок, а не точку\n getX($segment); // getX - функция, предназначенная для работы с точками\n ```\n\n3. Это преимущество немного неожиданно. Возможность вызывать методы у объектов открывает дорогу к автокомплиту в редакторах. Да-да! Именно благодаря такому способу вызова редактор может подсказать список методов, которые есть у данного объекта. Если вы сначала пишете функцию, а затем передаёте туда данные, то вы должны знать про существование функции заранее. Но тут нужно оговориться. Вызов функции после данных не является прерогативой ООП. В некоторых современных языках (Nim, D, Rust) поддерживается [Unified Function Call](https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax), который позволяет проделывать такой же трюк с обычными функциями. Ниже пример на языке [Nim](https://nim-lang.org/).\n\n ```nim\n # Создаётся тип Vector, представляющий из себя кортеж из двух элементов\n type Vector = tuple[x, y: int]\n\n # Определяется функция, принимающая на вход два вектора и возвращающая новый вектор,\n # полученный сложением исходных векторов\n proc add(a, b: Vector): Vector =\n (a.x + b.x, a.y + b.y)\n\n let\n # Создаётся переменная v1, содержащая вектор\n v1 = (x: -1, y: 4)\n # Создаётся переменная v2, содержащая вектор\n v2 = (x: 5, y: -2)\n\n # Обычный вызов функции\n v3 = add(v1, v2)\n\n # Вызов через точку: v1 передаётся в функцию add первым параметром\n v4 = v1.add(v2)\n\n # Цепочка вызовов. Результат предыдущего вычисления всегда передаётся первым параметром в следующий\n v5 = v1.add(v2).add(v1)\n ```\n\n4. Реализация ООП в PHP содержит конструкции для обеспечения _сокрытия данных_. Справедливости ради скажу, что, несмотря на это, их всегда можно обойти (например, используя **Reflection API**). Причём не только в PHP, но и в других языках с похожей моделью, например, в Java. С другой стороны, практика показывает, что отсутствие таких механизмов не создает больших проблем при работе. К таким языкам относится JavaScript.\n\n## Недостатки\n\n1. Инкапсуляция. Как и всегда в инженерной деятельности, за возможности нужно платить. Инкапсуляция, при всех своих плюсах, создает огромную проблему. Расширяемость поведения объекта падает до нуля. Если мы работаем с обычными функциями, то достаточно написать новую функцию, чтобы можно было продолжать работать. Когда речь заходит про инкапсуляцию, то всё не так. Дело в том, что методы описываются в классах. В PHP класс можно описать ровно один раз. И большая часть этих классов приходит в проекты из сторонних библиотек. Как только понадобится расширить поведение любого стороннего класса, мы сразу сталкиваемся с проблемами. Любой код из библиотек поставляется как есть, и мы не можем открыть исходный код любой библиотеки и внести необходимые нам правки. По этой причине расширяемость поведения объектов в ООП-языках — головная боль. Как правило, создатели класса пытаются заботиться об этом сами, давая возможность расширять своё поведение снаружи (если это возможно). Существуют языки, в которых эту проблему [пытаются решать](https://ru.wikipedia.org/wiki/Monkey_patch), позволяя \"дописывать\" определение класса по ходу работы программы, — например, в Ruby. В JS то же самое достигается за счёт механизма прототипов. Языки, в которых функции и данные разделены не имеют подобного недостатка, и код на них пишется, как ни странно, легче и проще (хотя местами многословнее). Сюда же можно отнести проблему, называемую антипаттерном (плохой реализацией) [Божественный объект или God object](https://ru.wikipedia.org/wiki/Божественный_объект).\n\n _Если вы уже немного знакомы с ООП, то можете подумать, что наследование спасает от этой проблемы. Так вот, наследование не просто не спасает, но и само по себе является проблемой. Об этом я расскажу в соответствующем курсе, когда мы разберём суть наследования как отношения между типами, и ограничениями, без которых наследование невозможно._\n\n2. Представление любой маломальской сущности в языке с помощью пользовательского типа сильно раздувает программу. А сущности часто создают только по той причине, что не нашлось подходящего под неё типа (в котором логично было бы описать её). Существует миф о том, что программы, написанные в ООП-стиле (на самом деле имеется в виду та самая модель ООП, которая используется в языках типа PHP или Java), при больших размерах оказываются относительно компактными по объёму кода. В реальности всё с точностью до наоборот. Программы на языке Clojure компактнее аналогов на PHP во много раз. И чем больше кода, тем больше разрыв. Эта тема настолько животрепещущая, что кто-то не поленился и создал проект-шутку [FizzBuzzEnterpriseEdition](https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition). К тому же появляются проблемы с ответственностями. Собака должна есть еду (`$dog->eat($food)`), или же еда съедается собакой (`$food->eatBy($dog)`)? Несмотря на кажущуюся абсурдность, подобная проблема реальна и проявляется очень часто.\n\n3. Думаю, что влияние этого пункта вы уже ощутили на себе, хотя мы только начали. Слишком много языковых сущностей ([Бритва Оккама](https://ru.wikipedia.org/wiki/%D0%91%D1%80%D0%B8%D1%82%D0%B2%D0%B0_%D0%9E%D0%BA%D0%BA%D0%B0%D0%BC%D0%B0)). В PHP постоянно добавляют новые возможности по реализации ООП. Вот лишь некоторые из них: абстрактные классы, анонимные классы, интерфейсы, статические методы, видимость методов, видимость свойств, видимость констант, трейты, магические методы, наследование. И это только ключевые слова. А все эти механизмы могут взаимодействовать друг с другом, порождая неведомые комбинации, у каждой из которых есть своё особенное поведение и ограничения. В итоге одно и то же поведение можно реализовать десятком разных способов. Приходится знать тысячи нюансов и постоянно решать споры о том, какой подход лучше.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":2333,"name":"theory","url":"/courses/php-introduction-to-oop/lessons/about/theory_unit"}],"links":[],"ordered_units":[{"id":2333,"name":"theory","url":"/courses/php-introduction-to-oop/lessons/about/theory_unit"}],"id":1110,"slug":"about","state":"approved","name":"О курсе","course_order":1,"goal":"Знакомимся с целями и задачами курса","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Этим курсом мы открываем цикл, посвящённый объектно-ориентированному программированию. Первый курс затрагивает самые основы, знакомит с базовым синтаксисом, концепциями и механизмом работы ООП в PHP. Следующие больше посвящены принципам написания программ в ООП-стиле, но и нового синтаксиса будет предостаточно.\n\nООП — большая тема, которой овладевают далеко не сразу. Пройдёт, возможно, не один год работы над реальными проектами, перед тем как картинка сложится.\n\nЭта часть курса содержит большое количество нового синтаксиса и терминов. Основные темы:\n\n* Объекты\n* Классы\n* Интерфейсы\n* Сокрытие данных (Data hiding)\n* Инкапсуляция\n* Автозагрузка\n* Исключения\n\nДанные темы крайне важны даже для начинающего PHP-разработчика потому, что с этими понятиями он начинает сталкиваться буквально с первых дней на новой работе. С другой стороны, требуется немало времени перед тем, как вы сможете действительно качественно использовать изучаемые подходы и техники в работе. К теме объектно-ориентированного программирования мы вернёмся в наших курсах ещё не раз и углубим не только наше понимание, но и разберём [оставшиеся понятия](https://php.net/manual/ru/language.oop5.php) вместе с их синтаксисом, например, абстрактные классы и трейты.\n"},"id":163,"slug":"php-introduction-to-oop","challenges_count":8,"name":"PHP: Введение в ООП","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы познакомитесь с объектно ориентированным программированием на PHP. Вы узнаете о классах, интерфейсах, инкапсуляции и магических методах. В итоге поймете, для чего нужны классы как абстракция данных. Знание основ объектно ориентированного программирования пригодится, чтобы понимать плюсы и минусы кода с классами и объектами. Также это поможет использовать объекты для реализации программной логики.","kind":"basic","updated_at":"2026-01-20T11:51:04.565Z","language":"php","duration_cache":73500,"skills":["Описывать сущности предметной области с помощью классов","Использовать объекты для реализации программной логики","Подключать механизм автозагрузки классов (стандарт PSR4) в composer","Защищать состояние объектов от внешнего воздействия с помощью модификаторов доступа","Понимать плюсы и минусы кода с классами и объектами","Использовать исключения в своем коде"],"keywords":["классы","инкапсуляция","интерфейсы","исключения","автозагрузка","магические методы"],"lessons_count":18,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6ODMyNiwicHVyIjoiYmxvYl9pZCJ9fQ==--9a298bb6a7915cc9145958490291b3af909fc177/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJwbmciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--6067466c2912ca31a17eddee04b8cf2a38c6ad17/image.png"},"recommendedLandings":[{"stack":{"id":2,"slug":"php","title":"PHP-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":60,"duration_in_months":10},"id":1,"slug":"php","title":"РНР-разработчик","subtitle":"Изучите PHP и Laravel для разработки и проектирования REST API","subtitle_for_lists":"Изучите PHP и Laravel для разработки и проектирования REST API","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"php","price_text":"от 5 650 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk5MiwicHVyIjoiYmxvYl9pZCJ9fQ==--e9d0f30948ea766a7e6bc3e3d56c192344d45fb8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programming-cuate%20(1).png"},{"stack":{"id":25,"slug":"php-oop","title":"ООП В PHP","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4300,"duration_in_months":2},"id":38,"slug":"php-oop","title":"ООП В PHP","subtitle":"Навык глубокого понимания архитектуры и написания чистого кода, позволяющий решать сложные задачи","subtitle_for_lists":"Изучите архитектуру и чистый код на PHP","locale":"ru","current":true,"duration_in_months_text":"2 месяца","stack_slug":"php-oop","price_text":"от 3 900 ₽","duration_text":"2 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1MSwicHVyIjoiYmxvYl9pZCJ9fQ==--e5793a1818ff43d73135cc7ed88c1998d7650470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-bro.png"},{"stack":{"id":468,"slug":"middle-php","title":"Middle-php разработчик","audience":"for_programmers","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"not_finished","order":2003,"duration_in_months":5},"id":596,"slug":"middle-php","title":"Middle-php разработчик","subtitle":"Освоите продвинутую работу с БД и серверной логикой","subtitle_for_lists":"Освоите продвинутую работу с БД и серверной логикой","locale":"ru","current":true,"duration_in_months_text":"5 месяцев","stack_slug":"middle-php","price_text":"от 4 050 ₽","duration_text":"5 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxNSwicHVyIjoiYmxvYl9pZCJ9fQ==--ee06f514098550b5ac7eebce6227b1543399a37c/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/php-introduction-to-oop/lessons/differences/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","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">PHP: Введение в ООП</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":"PHP: Введение в ООП"},"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><em>Изложенные ниже тезисы могут показаться вам совсем чуждыми и непонятными в силу отсутствия опыта. В этом нет ничего страшного, главное — увидеть направления, а отработкой мы займёмся в следующих курсах.</em></p>
<p>Перед тем как начать уходить глубже в тему объектов, хочу вас предостеречь. ООП в современном мире воспринимается большим числом программистов (особенно начинающими), как серебряная пуля, как средство от всех болезней. Учитывая, что в PHP это основной способ строить абстракции, у вас может сложиться такое же впечатление. Но это не так. Во-первых, под ООП понимают две абсолютно разные концепции. Та, которую мы обсуждаем, является мейнстримом, и встроена во многие языки настолько глубоко, что писать в другом стиле либо невозможно, либо очень тяжело. Но есть и другая, созданная <em>Аланом Кеем</em> (Alan Key). Что интересно, именно Алан является создателем термина ООП, но его ООП не имеет почти ничего общего с тем, что сейчас называется ООП.</p>
<blockquote>
<p>ООП для меня это сообщения, локальное удержание и защита, скрытие состояния и позднее связывание всего. Это можно сделать в Smalltalk и в LISP. Алан Кей</p>
</blockquote>
<p>Во-вторых, существуют другие способы получить поведение, похожее на то, которое вы будете наблюдать в ООП-коде. Более того, многие из них значительно мощнее в возможностях (и некоторые появились задолго до ООП-языков). Например, мультиметоды в языке Clojure дают большую свободу (мультидиспетчеризацию) и позволяют строить полиморфизм на специализированных функциях.</p>
<p>В общем и целом, чем больше разных по структуре языков и парадигм вы знаете, тем лучше понимаете, что происходит. Рекомендую: Clojure, Haskell, Elixir, Kotlin и JavaScript (последний стандарт).</p>
<h2 id="heading-2-1">Преимущества</h2>
<ol>
<li>
<p>Пожалуй, основное преимущество связано с <strong>полиморфизмом подтипов</strong>. Подробно эта тема освещается позже. Сейчас лишь скажу, что если мы вызываем функцию, то это всегда некоторая конкретная функция, импортированная из конкретного пространства имён. А вот если мы вызываем метод, то появляются варианты. Когда интерпретатор доходит до кода с вызовом метода <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$obj->methodCall()</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">$obj</code>. Отсюда следует, что, если разные объекты содержат методы с одинаковым именем (и сигнатурой), то их можно прозрачно (для вызывающего кода) подменять. На практике такая возможность местами упрощает код (становится меньше условных конструкций), но главное — делает его расширение проще.</p>
</li>
<li>
<p>Работа с абстракцией, основанной на ассоциативном массиве, таит в себе один сюрприз. Так как любая сущность представляется этим массивом, то можно по ошибке вызывать функцию, предназначенную для одной абстракции (например, точки), на другой абстракции (например, отрезка). Что при этом произойдёт — непонятно, и зависит от того, насколько удачно совпали структуры. И если такое произошло, то функция внезапно может отработать без ошибок и даже что-то вернуть. В итоге программа продолжит работать некорректно, вместо того, чтобы завершиться с ошибкой. Инкапсуляция исключает подобную ситуацию. Вызываемый метод всегда принадлежит тому объекту, на котором он вызывается. Если метода нет, то будет ошибка, если есть — то он отработает так, как и должен отработать. Но это преимущество является преимуществом только при сравнении с абстракциями, построенными на общих структурах данных (и в динамических языках), такими, как ассоциативные массивы. В языках с развитой системой типов (но без ООП), например, в Haskell, подобной проблемы также нет.</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"><?php
$segment = makeSegment(makePoint(1, 3), makePoint(10, 11));
// Функция отработает, хотя в неё передали отрезок, а не точку
getX($segment); // getX - функция, предназначенная для работы с точками</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>
</li>
<li>
<p>Это преимущество немного неожиданно. Возможность вызывать методы у объектов открывает дорогу к автокомплиту в редакторах. Да-да! Именно благодаря такому способу вызова редактор может подсказать список методов, которые есть у данного объекта. Если вы сначала пишете функцию, а затем передаёте туда данные, то вы должны знать про существование функции заранее. Но тут нужно оговориться. Вызов функции после данных не является прерогативой ООП. В некоторых современных языках (Nim, D, Rust) поддерживается <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax" rel="noopener noreferrer" target="_blank">Unified Function Call</a>, который позволяет проделывать такой же трюк с обычными функциями. Ниже пример на языке <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://nim-lang.org/" rel="noopener noreferrer" target="_blank">Nim</a>.</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"># Создаётся тип Vector, представляющий из себя кортеж из двух элементов
type Vector = tuple[x, y: int]
# Определяется функция, принимающая на вход два вектора и возвращающая новый вектор,
# полученный сложением исходных векторов
proc add(a, b: Vector): Vector =
(a.x + b.x, a.y + b.y)
let
# Создаётся переменная v1, содержащая вектор
v1 = (x: -1, y: 4)
# Создаётся переменная v2, содержащая вектор
v2 = (x: 5, y: -2)
# Обычный вызов функции
v3 = add(v1, v2)
# Вызов через точку: v1 передаётся в функцию add первым параметром
v4 = v1.add(v2)
# Цепочка вызовов. Результат предыдущего вычисления всегда передаётся первым параметром в следующий
v5 = v1.add(v2).add(v1)</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>
</li>
<li>
<p>Реализация ООП в PHP содержит конструкции для обеспечения <em>сокрытия данных</em>. Справедливости ради скажу, что, несмотря на это, их всегда можно обойти (например, используя <strong>Reflection API</strong>). Причём не только в PHP, но и в других языках с похожей моделью, например, в Java. С другой стороны, практика показывает, что отсутствие таких механизмов не создает больших проблем при работе. К таким языкам относится JavaScript.</p>
</li>
</ol>
<h2 id="heading-2-2">Недостатки</h2>
<ol>
<li>
<p>Инкапсуляция. Как и всегда в инженерной деятельности, за возможности нужно платить. Инкапсуляция, при всех своих плюсах, создает огромную проблему. Расширяемость поведения объекта падает до нуля. Если мы работаем с обычными функциями, то достаточно написать новую функцию, чтобы можно было продолжать работать. Когда речь заходит про инкапсуляцию, то всё не так. Дело в том, что методы описываются в классах. В PHP класс можно описать ровно один раз. И большая часть этих классов приходит в проекты из сторонних библиотек. Как только понадобится расширить поведение любого стороннего класса, мы сразу сталкиваемся с проблемами. Любой код из библиотек поставляется как есть, и мы не можем открыть исходный код любой библиотеки и внести необходимые нам правки. По этой причине расширяемость поведения объектов в ООП-языках — головная боль. Как правило, создатели класса пытаются заботиться об этом сами, давая возможность расширять своё поведение снаружи (если это возможно). Существуют языки, в которых эту проблему <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.wikipedia.org/wiki/Monkey_patch" rel="noopener noreferrer" target="_blank">пытаются решать</a>, позволяя "дописывать" определение класса по ходу работы программы, — например, в Ruby. В JS то же самое достигается за счёт механизма прототипов. Языки, в которых функции и данные разделены не имеют подобного недостатка, и код на них пишется, как ни странно, легче и проще (хотя местами многословнее). Сюда же можно отнести проблему, называемую антипаттерном (плохой реализацией) <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.wikipedia.org/wiki/%D0%91%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82" rel="noopener noreferrer" target="_blank">Божественный объект или God object</a>.</p>
<p><em>Если вы уже немного знакомы с ООП, то можете подумать, что наследование спасает от этой проблемы. Так вот, наследование не просто не спасает, но и само по себе является проблемой. Об этом я расскажу в соответствующем курсе, когда мы разберём суть наследования как отношения между типами, и ограничениями, без которых наследование невозможно.</em></p>
</li>
<li>
<p>Представление любой маломальской сущности в языке с помощью пользовательского типа сильно раздувает программу. А сущности часто создают только по той причине, что не нашлось подходящего под неё типа (в котором логично было бы описать её). Существует миф о том, что программы, написанные в ООП-стиле (на самом деле имеется в виду та самая модель ООП, которая используется в языках типа PHP или Java), при больших размерах оказываются относительно компактными по объёму кода. В реальности всё с точностью до наоборот. Программы на языке Clojure компактнее аналогов на PHP во много раз. И чем больше кода, тем больше разрыв. Эта тема настолько животрепещущая, что кто-то не поленился и создал проект-шутку <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition" rel="noopener noreferrer" target="_blank">FizzBuzzEnterpriseEdition</a>. К тому же появляются проблемы с ответственностями. Собака должна есть еду (<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$dog->eat($food)</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">$food->eatBy($dog)</code>)? Несмотря на кажущуюся абсурдность, подобная проблема реальна и проявляется очень часто.</p>
</li>
<li>
<p>Думаю, что влияние этого пункта вы уже ощутили на себе, хотя мы только начали. Слишком много языковых сущностей (<a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.wikipedia.org/wiki/%D0%91%D1%80%D0%B8%D1%82%D0%B2%D0%B0_%D0%9E%D0%BA%D0%BA%D0%B0%D0%BC%D0%B0" rel="noopener noreferrer" target="_blank">Бритва Оккама</a>). В PHP постоянно добавляют новые возможности по реализации ООП. Вот лишь некоторые из них: абстрактные классы, анонимные классы, интерфейсы, статические методы, видимость методов, видимость свойств, видимость констант, трейты, магические методы, наследование. И это только ключевые слова. А все эти механизмы могут взаимодействовать друг с другом, порождая неведомые комбинации, у каждой из которых есть своё особенное поведение и ограничения. В итоге одно и то же поведение можно реализовать десятком разных способов. Приходится знать тысячи нюансов и постоянно решать споры о том, какой подход лучше.</p>
</li>
</ol></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/php?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">10 месяцев</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">РНР-разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите PHP и Laravel для разработки и проектирования REST API</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/eyJfcmFpbHMiOnsiZGF0YSI6Mzk5MiwicHVyIjoiYmxvYl9pZCJ9fQ==--e9d0f30948ea766a7e6bc3e3d56c192344d45fb8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programming-cuate%20(1).png" alt="РНР-разработчик" 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">от 5 650 ₽</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="/programs/php-oop?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">2 месяца</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">ООП В PHP</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите архитектуру и чистый код на PHP</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/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1MSwicHVyIjoiYmxvYl9pZCJ9fQ==--e5793a1818ff43d73135cc7ed88c1998d7650470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-bro.png" alt="ООП В PHP" 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="/programs/middle-php?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">5 месяцев</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">Middle-php разработчик</p><p 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="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxNSwicHVyIjoiYmxvYl9pZCJ9fQ==--ee06f514098550b5ac7eebce6227b1543399a37c/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png" alt="Middle-php разработчик" 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">от 4 050 ₽</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/php-introduction-to-oop/lessons/differences/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 / 18</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/php-introduction-to-oop/lessons/differences/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-Bukl1lYy.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-BrRXra1y.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>