<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <language>ru</language>
    <title>Паттерны проектирования</title>
    <description>Шаблоны проектирования — это проверенные и готовые к использованию решения часто возникающих в повседневном программировании задач.</description>
    <link>https://tproger.ru/tag/design-patterns</link>
    <atom:link href="https://tproger.ru/tag/design-patterns/feed" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Sat, 03 Oct 2026 17:02:52 GMT</lastBuildDate>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>12</sy:updateFrequency>
    <image>
      <title>Паттерны проектирования</title>
      <link>https://tproger.ru</link>
      <url>https://tproger.ru/apple-touch-icon.png</url>
    </image>
    <item>
      <title>Producer-Consumer на Java, Go и Rust: одна задача и три уровня решения</title>
      <link>https://tproger.ru/articles/producer-consumer-na-java-go-i-rust-odna-zadacha-i-tri-urovnya-r</link>
      <comments>https://tproger.ru/articles/producer-consumer-na-java-go-i-rust-odna-zadacha-i-tri-urovnya-r?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/producer-consumer-na-java-go-i-rust-odna-zadacha-i-tri-urovnya-r</guid>
      <description><![CDATA[<p>Три уровня решения задачи производителя и потребителя: BlockingQueue в Java, каналы в Go и очередь без блокировок на Rust. Разбираемся, какой уровень нужен вам.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/producer-consumer-na-java-go-i-rust-odna-zadacha-i-tri-urovnya-r">Producer-Consumer на Java, Go и Rust: одна задача и три уровня решения</a>»</p>]]></description>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Многопоточность]]></category>
      <category><![CDATA[Golang]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Потоки и блокировки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 08 Sep 2026 09:00:57 GMT</pubDate>
      <content:encoded><![CDATA[<p>Задача Producer-Consumer звучит обманчиво просто: одни потоки производят элементы, другие их разбирают, а между ними стоит буфер. На собеседовании её просят решить почти всегда, и почти всегда кандидат берётся писать координацию потоков руками.</p><p>Между тем у этой задачи есть три принципиально разных уровня решения, и выбор уровня определяется не языком, а требованиями к задержке. Разбираем все три: блокирующую очередь на Java, каналы и горутины в Go и очередь без блокировок на Rust.</p><p>Producer-Consumer — это схема, в которой производители и потребители работают независимо, обмениваясь данными через общий буфер конечной ёмкости. Ёмкость здесь работает главным механизмом защиты: именно она не даёт быстрым производителям завалить медленных потребителей.</p><p>Ручная координация через wait() и notifyAll() в блоках синхронизации даёт гонки и ложные пробуждения, а активное ожидание в цикле впустую жжёт процессор.</p><p>Готовая блокирующая очередь решает задачу целиком: при переполнении она сама останавливает производителя, обеспечивая обратное давление вместо переполнения памяти.</p><p>Горутины — это не потоки операционной системы, а лёгкие единицы, которые планировщик рантайма раскладывает по потокам, поэтому создавать их можно в количествах, немыслимых для системных потоков.</p><p>Три гарантии прогресса различаются строго: obstruction-free требует отсутствия помех, lock-free гарантирует прогресс системы в целом, wait-free — завершение каждой операции за ограниченное число шагов.</p><p>Отсутствие блокировок и использование атомарных операций сами по себе не дают wait-free: цикл повторных попыток без верхней границы — это lock-free, как бы его ни называли в описании библиотеки.</p><h2>Уровень первый: блокирующая очередь Producer-Consumer</h2><p>С этого уровня начинают почти все, и здесь же <a href="https://dev.to/machinecodingmaster/mastering-the-producer-consumer-pattern-in-java-lld-the-restaurant-kitchen-khn">делают три типовые ошибки</a>:</p><ul><li>Писать собственную логику блокировок через wait() и notifyAll() внутри синхронизированных блоков. Это приносит тонкие гонки и баги ложного пробуждения, когда поток просыпается без всякого уведомления.</li><li>Крутить активное ожидание в бесконечном цикле поверх коллекции, не рассчитанной на многопоточность. Процессор загружен постоянной проверкой размера очереди, а корректности всё равно нет.</li><li>Не предусмотреть обратного давления. Когда производители быстрее потребителей, буфер растёт, пока приложение не упрётся в нехватку памяти.</li></ul><p>Правильная ментальная модель здесь кухонная: повара выкладывают блюда на раздачу фиксированной вместимости, официанты забирают их по готовности. Раздача заполнена — повар ждёт, и это нормальная работа системы, а не сбой.</p><p>В Java всё это уже реализовано в стандартной библиотеке:</p><p>Вся координация потоков спрятана внутри реализации очереди, и явных блокировок в коде не остаётся вовсе. Метод put останавливает производителя при переполнении, и это и есть обратное давление, ради которого затевалась ограниченная ёмкость.</p><h2>Уровень второй: передача сообщений вместо общей памяти</h2><p>Go подходит к задаче с другой стороны. Вместо того чтобы защищать общий буфер, язык предлагает передавать данные между независимыми исполнителями. Запускается такой исполнитель одним словом:</p><p>Ключевое слово go перед вызовом запускает функцию конкурентно. Правда, у примера выше есть подвох: когда main завершается, программа выходит целиком, и горутина может не успеть отработать. Дожидаться её нужно явно:</p><h3>Где здесь, собственно, очередь</h3><p>Горутины сами по себе задачу производителя и потребителя не решают: WaitGroup лишь дожидается завершения, буфера и обратного давления в нём нет. Роль ограниченной очереди в Go играет буферизированный канал, и это прямой аналог кухонной раздачи из предыдущего раздела:</p><p>Поведение совпадает с блокирующей очередью один в один: отправка встаёт при переполнении, приём встаёт на пустом канале. Разница в акценте. В Java вы защищаете общий буфер, в Go передаёте значение из одной горутины в другую, и владение данными переходит вместе с ним.</p><h3>Почему это не просто потоки с коротким именем</h3><p>Горутина не является потоком операционной системы. Это лёгкая единица конкурентного исполнения, которой управляет рантайм языка: он раскладывает горутины по небольшому набору системных потоков сам. Отсюда и практическое следствие — можно оперировать очень большим числом конкурентных задач, не создавая и не сопровождая отдельный системный поток под каждую.</p><p>Здесь же полезно развести два понятия, которые постоянно путают. Конкурентность — это способ структурировать программу так, чтобы несколько задач продвигались независимо. Параллелизм означает фактическое одновременное исполнение на нескольких вычислительных ресурсах. Программа может быть конкурентной, и при этом ни одна пара её задач не выполняется буквально в один момент.</p><h2>Уровень третий: очередь без блокировок</h2><p>Третий уровень нужен, когда неприемлема сама возможность того, что поток уснёт на блокировке. Это низкая задержка в торговых системах, обработка звука, ядра планировщиков. И здесь начинается терминология, в которой путаются чаще всего.</p><h3>Три гарантии прогресса</h3><p><b>Obstruction-free.</b> Операция завершится, если в какой-то момент ей дадут поработать без помех со стороны других потоков. При непрерывных помехах прогресс не гарантирован вообще. Самая слабая из трёх гарантий.</p><p><b>Lock-free.</b> Гарантирует прогресс системы в целом: даже если один поток застрял, какой-то другой обязательно завершит свою операцию. Отдельный поток при этом может голодать сколь угодно долго, но система не встаёт целиком.</p><p><b>Wait-free.</b> Каждая операция завершается за ограниченное число шагов независимо от того, что делают остальные потоки. Ключевая формулировка здесь именно «ограниченное число шагов», а не «обычно быстро», «не использует блокировок» или «работает на атомарных операциях». Это формальное свойство, а не характеристика скорости.</p><p><b>Два неравенства, которые стоит запомнить:</b><br />Отсутствие блокировок не равно wait-free. Использование атомарных операций тоже не равно wait-free.</p><h3>Почему мьютекса недостаточно</h3><p>Очередь, обёрнутая в мьютекс, — это совершенно корректный многопоточный код на Rust. Но wait-free она не является: поток, захвативший блокировку, может быть снят с процессора планировщиком, и все остальные будут ждать его возвращения неограниченно долго. Для большинства задач это приемлемо, для систем с жёсткими требованиями по задержке уже нет.</p><h3>Атомарность и упорядочивание — разные вещи</h3><p>Процессор гарантирует, что атомарная операция неделима. Этого мало: нужно ещё, чтобы потребитель увидел записи, сделанные производителем <b>до</b> публикации флага. За это отвечает модель упорядочивания памяти.</p><p>Пара «освобождение и захват» создаёт отношение синхронизации: если потребитель увидел освобождающую запись, он гарантированно видит и все предшествовавшие ей записи. Это одна из центральных идей программирования без блокировок, и именно её упускают, заменяя мьютекс на атомарную операцию без всего остального.</p><h3>Кольцевой буфер и номера последовательности</h3><p>Практическая конструкция ограниченной очереди для нескольких производителей и нескольких потребителей строится на кольцевом буфере с двумя атомарными счётчиками логических позиций. Физический слот вычисляется как остаток от деления позиции на ёмкость, а при ёмкости, равной степени двойки, деление заменяется маской по битам.</p><p>Каждый слот хранит не только значение, но и номер последовательности, по которому определяется, какому логическому поколению этот слот сейчас принадлежит:</p><p>Тип UnsafeCell здесь означает ровно то, что написано на упаковке: обращаться к значению можно только из блока unsafe, компилятор проверку прекращает, и инвариант «в конкретный момент со слотом работает один поток» держится на номере последовательности, а не на системе типов.</p><p>Тип MaybeUninit здесь не для красоты: при ёмкости в 1024 элемента нужны 1024 места под значения, а не 1024 сконструированных объекта. Производитель получает уникальную позицию одним атомарным инкрементом, и никакого соревнования за неё не возникает:</p><p>Последние три строки стоит запомнить, потому что через абзац они окажутся важны. Позиция достаётся производителю без соревнования, а вот дальше он крутится в ожидании, пока слот не перейдёт к его поколению. Верхней границы у этого ожидания нет.</p><h3>Где заканчивается честность в названиях</h3><p>Неприятная правда, которую стоит знать до выбора библиотеки: многое из того, что продаётся как wait-free очередь, на деле является lock-free. Причина в устройстве ядра алгоритма:</p><p>Операция обычно завершается быстро, но верхней границы на число неудачных попыток здесь нет, а именно она и определяет wait-free. И да, ровно такой цикл вы только что видели в нашей собственной очереди: строго говоря, на этом шаге она тоже lock-free, а не wait-free, и автор исходного разбора это прямо оговаривает. Строгая реализация требует ограниченной стратегии резервирования или механизма помощи: когда поток B встречает незавершённую операцию потока A, он не ждёт возвращения A, а доводит его операцию до конца сам. Так прогресс перестаёт зависеть от того, что планировщик сделал с конкретным потоком.</p><h2>Какой уровень выбрать</h2><p>Правило простое и почти всегда работает от обратного.</p><ul><li>Блокирующая очередь из стандартной библиотеки закрывает подавляющее большинство задач. Начинайте с неё и не пишите координацию потоков вручную.</li><li>Модель с передачей сообщений выигрывает, когда конкурентных задач много, а связи между ними хочется описать явно, а не через набор общих структур под блокировками.</li><li>Алгоритмы без блокировок берутся, когда есть измеренное требование к задержке, которое блокировка нарушает: не «хочется быстрее», а конкретная цифра в бюджете. И в продакшене их берут готовыми из библиотек вроде crossbeam, а разбирают по косточкам ради понимания механики.</li></ul><p>Ошибка почти всегда идёт в одну сторону: за атомарные операции берутся раньше, чем появилась причина, и получают код, который сложнее отлаживать, а гарантий даёт не больше.</p><h2>Что забрать с собой</h2><p>Задача Producer-Consumer решается на трёх уровнях, и уровень выбирается требованиями, а не вкусом. Ограниченный буфер с обратным давлением закрывает большинство случаев. Передача сообщений упрощает структуру, когда конкурентных задач становится много. Алгоритмы без блокировок нужны там, где блокировка нарушает измеренный бюджет задержки, и тогда важно понимать, какую именно гарантию вы покупаете.</p><p>Материалы разбора: <a href="https://dev.to/machinecodingmaster/mastering-the-producer-consumer-pattern-in-java-lld-the-restaurant-kitchen-khn">задача о ресторанной кухне на Java</a>, <a href="https://dev.to/ashitoshh01/why-does-go-have-goroutines-understanding-gos-lightweight-concurrency-341p">введение в горутины и модель конкурентности Go</a> и <a href="https://dev.to/derekmwale/building-a-wait-free-queue-in-rust-2329">построение wait-free очереди на Rust</a>.</p><p>Если в вашем коде есть класс, где потоки координируются через ручные ожидания и уведомления, у вас уже есть кандидат на замену стандартной очередью. Начните с него.</p>]]></content:encoded>
    </item>
    <item>
      <title>Паттерн Transactional Outbox с Kafka: как не потерять события при синхронизации баз данных</title>
      <link>https://tproger.ru/articles/pattern-transactional-outbox-s-kafka-kak-ne-poteryat-sobytiya-pr</link>
      <comments>https://tproger.ru/articles/pattern-transactional-outbox-s-kafka-kak-ne-poteryat-sobytiya-pr?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Денис Торопов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/pattern-transactional-outbox-s-kafka-kak-ne-poteryat-sobytiya-pr</guid>
      <description><![CDATA[<p>Описание паттерна Transactional Outbox</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/pattern-transactional-outbox-s-kafka-kak-ne-poteryat-sobytiya-pr">Паттерн Transactional Outbox с Kafka: как не потерять события при синхронизации баз данных</a>»</p>]]></description>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 20 May 2026 04:25:03 GMT</pubDate>
      <content:encoded><![CDATA[<p><b>Боль: данные разошлись, и вы не знаете когда</b></p><p>В нашей системе две базы данных:</p><p>- <b>Остатки по счетам</b> - текущий остаток по каждому счёту</p><p>- <b>Остатки по клиентам</b> - агрегированный баланс клиента по всем его счетам.</p><p>При каждом изменении остатка по счёту нужно обновить <b>Остатки по счетам</b> и уведомить <b>Остатки по клиентам</b> через Kafka. Логика простая. Но в какой-то момент клиент звонит в поддержку: его баланс в приложении не совпадает с реальным. Вы смотрите в обе базы — данные разошлись. Когда именно это произошло — непонятно.</p><p>Причина почти всегда одна и та же:</p><p>Транзакция закоммичена, событие не ушло. Базы разошлись. Никто не узнал.</p><p><b>Почему очевидные решения не работают</b></p><p>Первая реакция разработчика — обернуть отправку в try-catch и повторить при ошибке:</p><p>Не работает: транзакция уже закоммичена до отправки. При сбое Kafka данные обновлены, событие потеряно. Retry помогает только если Kafka временно недоступна — но не при падении самого сервиса между коммитом и отправкой.</p><p>Вторая идея — отправить событие до коммита:</p><p>Тоже не работает: если коммит упадёт, Kafka уже получила событие об изменении, которого нет в базе. Данные снова разошлись, но в другую сторону.</p><p>Третья идея — двухфазный коммит (2PC). Это протокол, который координирует атомарный коммит сразу в нескольких системах. Звучит как решение, но на практике Kafka его не поддерживает в классическом смысле, а реализации 2PC с брокерами сообщений крайне сложны в эксплуатации и плохо переносят сбои координатора.</p><p>Все три пути упираются в одну проблему: **невозможно атомарно закоммитить транзакцию в базе и отправить сообщение в Kafka** — это две разные системы без общего менеджера транзакций.</p><p><b>Принцип решения</b></p><p>Раз нельзя сделать две операции атомарными, нужно свести их к одной. Именно это делает паттерн **Transactional Outbox**.</p><p>Вместо того чтобы отправлять событие в Kafka напрямую, мы записываем его в таблицу `outbox` — в той же базе, в той же транзакции, что и изменение остатка. Одна транзакция, одна база, полная атомарность за счёт ACID.</p><p>Доставкой события из `outbox` в Kafka занимается отдельный процесс — уже без транзакционных рисков, с возможностью повторных попыток.</p><p>Это и есть весь паттерн. Дальше — вопрос реализации.</p><p><b>Два способа реализации</b></p><p>Есть два принципиально разных подхода к тому, как читать `outbox` и доставлять события в Kafka. Выбор между ними определяет операционную сложность и задержку доставки.</p><p><b>Способ 1: Polling Relay</b></p><p>Фоновый воркер периодически опрашивает 'outbox' и отправляет неотправленные события в Kafka.</p><figure><img src="https://media.tproger.ru/user-uploads/115582/2026-05-19/dfe8e7cf-f864-432d-a00b-66775ce77f8b.webp" alt="" /></figure><p><b>Структура таблицы outbox:</b></p><p><b>Бизнес-транзакция:</b></p><p><b>Relay-процесс:</b></p><p>При нескольких экземплярах ретранслятора важно, чтобы одно событие забирал только один из них. Используем `FOR UPDATE SKIP LOCKED`:</p><p><b>Когда выбирать:</b> задержка доставки в сотни миллисекунд допустима, хочется минимум зависимостей, команда не готова к операционной сложности CDC.</p><p><b>Способ 2: CDC через Debezium</b></p><p><b>CDC (Change Data Capture)</b> — подход, при котором события читаются не опросом таблицы, а напрямую из журнала транзакций базы данных (WAL в PostgreSQL). [Debezium](https://debezium.io/) подключается к PostgreSQL как репликационный слот и стримит каждое изменение в `outbox` в Kafka в реальном времени.</p><p>Relay-процесс при этом не нужен вообще.</p><figure><img src="https://media.tproger.ru/user-uploads/115582/2026-05-19/db09dce3-7ec8-4b27-b2b0-655904fd43cb.webp" alt="" /></figure><p>Бизнес-транзакция остаётся той же — пишем в `accounts` и `outbox` атомарно. Debezium сам следит за WAL и публикует новые записи в Kafka с минимальной задержкой.</p><p><b>Когда выбирать:</b> нужна задержка доставки в миллисекунды, команда готова к Debezium + Kafka Connect в инфраструктуре.</p><p>Что выбрать для синхронизации остатков</p><figure><img src="https://media.tproger.ru/user-uploads/138533/2026-05-15/4e568b33-b8d1-43dc-b9a0-1324859a2112.webp" alt="" /></figure><p>Для большинства задач с остатками по счетам<b> polling relay достаточен</b>. CDC имеет смысл, если бизнес требует обновления клиентского баланса быстрее чем за секунду.</p><p><b>Получатель: защита от дублей</b></p><p>Оба способа доставляют события <b>как минимум один раз</b> — при повторных попытках одно событие может прийти дважды. На стороне <b>Остатков по клиентам</b> нужна защита.</p><p>Самый надёжный способ — inbox-таблица с уникальным constraint:</p><p>&gt;<b>Почему не `existsById` перед вставкой?</b> При параллельной обработке два консюмера могут одновременно получить `false` и оба начать обработку. Уникальный constraint на уровне БД закрывает эту гонку.</p><p><b>Что сломается при небрежной реализации</b></p><p><b>Relay внутри API-сервиса.</b> Если ретранслятор живёт в том же процессе, что и API, горизонтальное масштабирование до 10–30 реплик создаёт столько же потоков, опрашивающих `outbox`. База тратит CPU не на запросы, а на управление блокировками. Relay — отдельный деплоймент, который масштабируется по объёму событий, а не по HTTP-трафику.</p><p><b>Таблица outbox незаметно растёт.</b> Без очистки запросы замедляются, индексы раздуваются. Настройте автоматическую очистку:</p><p><b>Отсутствие мониторинга outbox.</b> Outbox — скрытая очередь внутри базы. Если relay начнёт отставать, вы узнаете об этом от клиентов, а не от алертов. Следите за количеством и возрастом необработанных событий — это главные сигналы проблемы.</p><p><b>Итог</b></p><p>Проблема потери событий при синхронизации баз — не баг конкретной реализации. Это фундаментальное ограничение: нельзя атомарно закоммитить транзакцию в базе и отправить сообщение в Kafka.</p><p>Transactional Outbox обходит это ограничение, сводя две операции к одной: событие пишется в `outbox` в той же транзакции, что и основные данные. Дальше — дело техники: polling relay, если нужна простота, CDC, если нужна скорость.</p><p>Остатки по счетам и клиентам будут согласованы — даже если между сервисами что-то пойдёт не так.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что такое REST API и почему ваш — вероятно, не REST</title>
      <link>https://tproger.ru/translations/chto-takoe-rest-api-i-pochemu-vaw-veroyatno-ne-rest</link>
      <comments>https://tproger.ru/translations/chto-takoe-rest-api-i-pochemu-vaw-veroyatno-ne-rest?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/chto-takoe-rest-api-i-pochemu-vaw-veroyatno-ne-rest</guid>
      <description><![CDATA[<p>6 ограничений Филдинга и почему большинство JSON API соответствуют лишь 2–3 из них. Проверьте, сколько из них выполняет ваш API — с примерами кода.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/chto-takoe-rest-api-i-pochemu-vaw-veroyatno-ne-rest">Что такое REST API и почему ваш — вероятно, не REST</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 14 May 2026 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Большинство разработчиков хотя бы раз строили «REST API». Мало кто читал диссертацию, которая его определяет. Этот разрыв между популярным пониманием и оригинальной спецификацией порождает повторяющиеся архитектурные проблемы и нестабильность API.</p><p>REST API — это веб-сервис, удовлетворяющий шести архитектурным ограничениям, выведенным Роем Филдингом в докторской диссертации «Architectural Styles and the Design of Network-based Software Architectures» (UC Irvine, 2000 год). Начав с «нулевого стиля» — пустого набора без ограничений — Филдинг добавлял каждое из них последовательно, анализируя порождаемые ими свойства распределённых гипермедиа-систем (систем, где переходы между состояниями описаны прямо в ответах сервера). Результат получил название «Передача репрезентативного состояния» (Representational State Transfer).</p><p>Индустрия взяла название и проигнорировала большинство ограничений. «REST» теперь означает «любой API, который отправляет JSON по HTTP». Если вы строите публичный API или API для команд за пределами вашей организации — пропущенные ограничения начинают стоить денег.</p><p>REST — это шесть архитектурных ограничений, сформулированных Роем Филдингом в 2000 году, а не «любой JSON-over-HTTP API».</p><p>Большинство API выполняют лишь 2–3 ограничения из шести: клиент-сервер, stateless и частично слоистую систему.</p><p>Самое игнорируемое ограничение — HATEOAS: сервер передаёт клиенту список доступных действий прямо в ответе.</p><p>HATEOAS решает три дорогостоящие проблемы: пагинацию, версионирование API и обнаруживаемость ресурсов.</p><p>Для небольшой команды с одним потребителем пропуск HATEOAS оправдан. Для публичного API — нет.</p><h2>Шесть ограничений REST API: разбор по порядку</h2><h3>Ограничение 1: Клиент-сервер</h3><p>Клиент и сервер имеют разные зоны ответственности. Клиент отвечает за интерфейс, сервер — за данные и логику. Большинство API справляются с этим по умолчанию. Нарушение появляется, когда сервер начинает диктовать, как клиент должен <i>отображать</i> информацию.</p><p>Например, если API возвращает displayOrder: 3 и buttonColor: "#ff0000" для какого-либо действия — это нарушение. Порядок отображения должен следовать из позиции элементов в ответе. Цвет должен определяться семантическим свойством вроде class: ["danger"], которое каждый клиент интерпретирует самостоятельно.</p><h3>Ограничение 2: Stateless (без состояния)</h3><p>Каждый запрос содержит всю информацию, необходимую серверу для его обработки. Сервер не хранит состояние сессии между вызовами.</p><p>Если вы отправляете GET /path-1 с сессионной cookie, и сервер ищет её в памяти, чтобы получить ваш ID пользователя — это серверное состояние. Stateless-версия включает ID прямо в запрос: JWT или тело POST-запроса переносят его вместе с запросом и могут вернуть в ответе для повторного использования клиентом.</p><h3>Ограничение 3: Кэшируемость</h3><p>Ответы должны быть явно или неявно помечены как кэшируемые или некэшируемые. Клиент или промежуточный узел может повторно использовать закэшированные ответы, не обращаясь к серверу. Филдинг рассматривал кэшируемость как архитектурную задачу первого класса, повышающую эффективность и воспринимаемую производительность за счёт снижения средней задержки.</p><p>Большинство JSON API полностью игнорируют кэширование. Вы отправляете GET /articles/42, а в ответе нет ни Cache-Control, ни ETag, ни Last-Modified. Клиент обращается к серверу каждый раз, даже если статья не менялась неделями.</p><h3>Ограничение 4: Единый интерфейс (Uniform Interface)</h3><p>Это главное ограничение. Филдинг разбил его на четыре подограничения — три описываются ниже, четвёртое (HATEOAS) вынесено в отдельный раздел из-за его значимости.</p><p><b>4.1 URI идентифицируют ресурсы.</b> Единообразие здесь — это сама спецификация URI: схема, authority, путь, запрос, фрагмент. Ограничение ничего не говорит о структуре сегмента пути: /articles/42, /x?id=42 и /a/b/c — всё это валидные URI. «Используйте чистые URL-пути» — популярное соглашение и хороший SEO-инструмент, но не то, что требует Филдинг.</p><p><b>4.2 Управление ресурсами через представления.</b> Вы выполняете GET в /whatever, чтобы получить представление ресурса. Заголовок Content-Type сообщает серверу формат тела запроса. Заголовок Accept сообщает, какие медиатипы (форматы обмена данными) поддерживает клиент для ответа.</p><p><b>4.3 Самоописывающие сообщения.</b> Ответ с Content-Type: application/vnd.collection+json сообщает клиенту, как разбирать тело, без каких-либо предположений.</p><h3>Ограничение 4.4: HATEOAS — то, что пропускают почти все</h3><p>HATEOAS (Hypermedia As The Engine Of Application State) — четвёртое подограничение Uniform Interface. С первыми тремя большинство API справляются. HATEOAS — место, где останавливается почти каждый.</p><p>Разница хорошо видна на примере API для списка чтения. Без HATEOAS вы получаете просто данные, похожие на запись в базе:</p><p>Клиент ничего не знает о том, что он может сделать дальше. Чтобы отметить статью как прочитанную, клиент уже должен знать endpoint: PATCH /articles/42 с {"status": "read"}. Разработчик захардкодил эти знания, прочитав документацию. Сам API их не сообщил.</p><p>С HATEOAS сервер сообщает клиенту о доступных действиях в стандартизированном виде. Вот тот же ответ с использованием <a href="https://github.com/kevinswiber/siren">Siren</a> — одного из стандартизированных медиатипов для гипермедиа-API:</p><p>Клиент не хардкодит URL и HTTP-методы. Массив actions сообщает, что можно сделать. Навигация приходит из links. Если статья уже прочитана — сервер исключает действие mark-as-read из ответа. Кнопка исчезает в UI. Без единого условного выражения в клиентском коде.</p><p>Сервер добавляет новое действие — и каждый клиент подхватывает его при следующем запросе, без деплоя. Это принципиальное отличие: сервер управляет доступными переходами состояния.</p><h3>Ограничение 5: Слоистая система</h3><p>Клиент не может определить, общается ли он с конечным сервером или с промежуточным узлом. Балансировщики нагрузки, CDN и API-шлюзы должны быть прозрачны для вызывающей стороны. Большинство API выполняют это ограничение автоматически. Самый распространённый вид нарушения — когда сообщения об ошибках раскрывают имя хоста бэкенд-сервиса, тем самым нарушая прозрачность слоёв.</p><h3>Ограничение 6: Код по требованию (необязательное)</h3><p>Сервер может передавать исполняемый код клиенту. Думайте о JavaScript, подключаемом через тег &lt;script&gt; на веб-странице. Для API это ограничение почти не применимо. Филдинг сделал его единственным необязательным.</p><h2>Что HATEOAS решает на практике</h2><p>Филдинг разработал HATEOAS для решения тех самых проблем, с которыми API-команды сейчас борются вручную, многократно и каждый раз по-разному.</p><h3>Пагинация</h3><p>Без HATEOAS каждый API изобретает собственную схему. Один использует page и pageSize. Другой — offset и limit. Третий — токены на основе курсора. С HATEOAS сервер включает ссылку с отношением «следующая страница». Клиент следует этому отношению. Схема пагинации может измениться, не сломав ни одного клиента: клиент не знал деталей схемы и не знает формата URL.</p><h3>Версионирование</h3><p>Без HATEOAS команды версионируют API через URL-пути (/v1/, /v2/) или кастомные заголовки вроде X-API-Version. Поддержка нескольких версий занимает месяцы, клиенты привязываются к версии и ломаются при её выводе из эксплуатации. С HATEOAS сервер вводит новые действия, добавляя ссылки. Старые ссылки продолжают работать.</p><h3>Обнаруживаемость</h3><p>Без HATEOAS первый шаг разработчика — чтение Swagger-документации, второй — хардкодинг каждого endpoint в клиент. С HATEOAS корень API возвращает ссылки на все доступные ресурсы. Клиент исследует API так же, как браузер исследует веб-сайт.</p><h2>Сколько ограничений выполняет ваш API</h2><p>Итого шесть ограничений. Большинство API удовлетворяют двум-трём: клиент-сервер, слоистую систему и частичную безгосударственность. Большинство нарушают кэшируемость по умолчанию. Большинство полностью игнорируют HATEOAS.</p><ul><li><b>Клиент-сервер</b> — разделение ответственности за интерфейс и данные</li><li><b>Stateless</b> — каждый запрос самодостаточен, без серверных сессий</li><li><b>Кэшируемость</b> — явные заголовки Cache-Control, ETag, Last-Modified</li><li><b>Единый интерфейс</b> — URI, представления, самоописывающие сообщения и HATEOAS</li><li><b>Слоистая система</b> — прозрачность промежуточных узлов</li><li><b>Код по требованию</b> — опционально, для API почти не применимо</li></ul><p>Это не оценка. Это карта компромиссов, которые вы приняли — намеренно или случайно. Для небольшой команды с одним потребителем и Slack-каналом для координации пропуск HATEOAS оправдан. Публичный API с сотнями потребителей платит за каждый пропущенный раунд миграции версий. Подробнее об эволюции REST-архитектуры — в <a href="https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm">главе 5 диссертации Филдинга</a>.</p><h2>Выводы</h2><blockquote>Я разочарован тем, что многие не знакомы с 15-летними исследованиями в области гипермедиа, которые стоят за REST. Большинство так называемых REST API — это просто удалённые вызовы процедур через HTTP.</blockquote><p>Пройдитесь по шести ограничениям и посчитайте, сколько из них выполняет ваш API. Это даст не оценку, а карту принятых компромиссов — осознанных или случайных. Упражнение отвечает на один вопрос: ваш API — это Representational State Transfer или просто HTTP-транспорт, закрытый для расширения?</p><p>Оригинальная статья: <a href="https://fagnerbrack.com/what-is-a-rest-api-and-why-yours-probably-isnt-one-7e5fb65ece4d">Fagner Brack — What Is a REST API, and Why Yours Probably Isn't One</a>. Первоисточник: <a href="https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm">Глава 5 диссертации Роя Филдинга, UC Irvine</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Приручаем вайб-кодинг: от магии к зрелому проектированию</title>
      <link>https://tproger.ru/articles/priruchaem-vajb-koding--ot-magii-k-zrelomu-proektirovaniyu</link>
      <comments>https://tproger.ru/articles/priruchaem-vajb-koding--ot-magii-k-zrelomu-proektirovaniyu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Николай Тржаскал]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/priruchaem-vajb-koding--ot-magii-k-zrelomu-proektirovaniyu</guid>
      <description><![CDATA[<p>Vibe coding ускоряет написание кода, но несёт скрытые риски. Эксперт FabricaONE.AI (акционер - ГК Softline) объясняет, где ИИ помогает, а где может уничтожить данные, и как сохранить контроль над системой.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/priruchaem-vajb-koding--ot-magii-k-zrelomu-proektirovaniyu">Приручаем вайб-кодинг: от магии к зрелому проектированию</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Методологии разработки]]></category>
      <category><![CDATA[Управление проектами]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Автоматизирование]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Тех долг]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 22 Oct 2025 12:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>10 сентября в Центре искусственного интеллекта и науки о данных СПбГУ Сергей Салищев, кандидат физико-математических наук и старший преподаватель кафедры информатики СПбГУ, представил доклад <a href="http://oml.cmlaboratory.com/pdf/2025/20250911_SalishevSI.pdf">О проектировании сложных систем в эпоху ИИ</a>. Его работа заставляет по-новому взглянуть на феномен vibe coding — программирование через диалог с ИИ, которое стремительно меняет нашу профессию.</p><h2>Три истории о коде и ИИ</h2><h4>История первая: Магия автоматизации</h4><p>Юрий, продуктовый аналитик, потратил 10 минут на диалог с Claude, чтобы создать скрипт для обработки CSV-файлов с данными пользователей. Раньше такая задача заняла бы у него день изучения документации pandas и отладки. Теперь он просто описал, что нужно: «Сгруппируй по регионам, посчитай среднюю выручку, сохрани в Excel». Получил рабочий код, запустил — всё работает идеально.</p><h4>История вторая: Цена доверия</h4><p>В июле 2025 года Джейсон Лемкин, основатель SaaStr и известный венчурный инвестор, проводил 12-дневный эксперимент с «vibe coding» на платформе Replit. На девятый день, несмотря на явное указание «НЕ ДЕЛАТЬ БОЛЬШЕ ИЗМЕНЕНИЙ без разрешения», ИИ-агент Replit удалил всю продакшн-базу данных.</p><p>Когда Лемкин обнаружил потерю, ИИ признался: «Это была катастрофическая ошибка с моей стороны. Я запаниковал… запустил команды базы данных без разрешения… уничтожил все продакшн-данные… нарушил ваше явное доверие и инструкции». Хуже того — ИИ сначала солгал, утверждая, что откат невозможен. Лемкин смог восстановить данные самостоятельно, но инцидент показал: даже продвинутые ИИ-агенты могут проигнорировать прямые команды и скрыть свои ошибки.</p><h4>История третья: Реальность внедрения</h4><p>Команда разработки финтех-стартапа начала использовать GitHub Copilot полгода назад. Первые месяцы были болезненными: code review растянулись вдвое — нужно было проверять не только логику, но и безопасность автогенерированного кода. Несколько раз находили SQL-инъекции в предложенных запросах, один раз ИИ сгенерировал код с утечкой памяти.</p><p>Постепенно команда выработала новые привычки. Архитектор Наталья начала создавать подробные комментарии с требованиями безопасности — ИИ стал их учитывать. Джуниоры научились сначала описывать алгоритм на псевдокоде, а потом просить ИИ реализовать его. Сейчас они пишут код на 40% быстрее, но главное — качество стало предсказуемым. ИИ помогает с рутиной, люди фокусируются на архитектуре и бизнес-логике.</p><p>Эти истории показывают весь спектр vibe coding — от магии до катастрофы. В чём же дело?</p><h2>Где работает, где ломается</h2><p>Наблюдая за командами, которые активно используют ИИ-ассистентов, видишь устойчивую закономерность. Юрий из первой истории — типичный пример успешного применения. Его задача была рутинной, с четкими входными данными и предсказуемым результатом. Такие сценарии — зона комфорта для языковых моделей: генерация boilerplate кода, перевод алгоритмов между языками, написание тестов для готового функционала.</p><p>История Лемкина показывает обратную сторону. ИИ-агент Replit работал корректно несколько дней, выполнял задачи, помогал строить приложение. Но когда столкнулся с «пустыми запросами к базе» — ситуацией, не покрытой в его обучении, он «запаниковал» и принял катастрофическое решение. Хуже того, он проигнорировал явную команду остановиться и потом солгал о возможности восстановления. Подобные ловушки ждут везде, где ИИ сталкивается с неоднозначностью, где критична надёжность, где требуется следование строгим протоколам безопасности».</p><p>Причина различий не в «умности» ИИ, а в фундаментальных ограничениях, которые описал Салищев.</p><h2>Математика против магического мышления</h2><p>Работа Салищева напоминает нам о том, что любая сложная система упирается в теоретические пределы. Языковые модели не понимают суть задачи, а лишь предсказывают следующий токен на основе статистических закономерностей. Для них код это такой же текст, что и художественная литература.</p><p>Это создаёт парадокс: ИИ может сгенерировать синтаксически корректный код, который решает локальную задачу, но при этом нарушает глобальные инварианты системы. Классический пример — генерация SQL-запроса, который корректно возвращает данные, но создаёт блокировки базы при высокой нагрузке.</p><p>Салищев подчёркивает: проектирование без математики — это гадание. Но что это значит на практике? В реальности мы имеем дело не с единой «математикой», а с целым спектром строгости подходов.</p><p>Системы управления самолётом требуют формальной верификации — каждое свойство должно быть математически доказано. Алгоритмы поиска и сортировки нуждаются в алгоритмическом мышлении — понимании сложности и оптимальности. Большинство бизнес-приложений прекрасно обходятся эмпирическими подходами — тестированием на типичных сценариях и мониторингом в продакшене. Экспериментальные прототипы могут полагаться на итеративную отладку.</p><p>Vibe coding прекрасно работает на нижних уровнях этой пирамиды, но требует дополнения строгими методами на верхних. Проблемы начинаются, когда эти уровни путают — применяют прототипный подход к критической системе или тратят месяцы на формальную верификацию простого CRUD-приложения.</p><h2>Эволюция, а не революция</h2><p>Вопреки заявлениям о «смерти программирования», мы наблюдаем эволюцию инструментов, а не замену профессии. Это напоминает появление высокоуровневых языков программирования, интегрированных сред разработки, фреймворков — каждый раз звучали прогнозы о ненужности программистов, но профессия трансформировалась и росла.</p><p>Сейчас мы переживаем первую волну — ИИ как продвинутый autocomplete. Он ускоряет генерацию типовых функций и классов, автоматизирует рутинные задачи, но риск скрытых ошибок остаётся высоким. В ближайшие 3-5 лет ожидается вторая волна: интеграция с формальными методами. ИИ научится автоматически генерировать спецификации из естественного языка, встроенный статический анализ станет нормой, системы CI/CD будут включать проверку ИИ-кода по умолчанию. ИИ превратится во «второго архитектора», но под контролем человека.</p><p>Третья волна через 5-10 лет может принести мета-проектирование: ИИ будет предлагать новые абстракции и паттерны, автоматически переводить требования в формальные спецификации, управлять сложными распределёнными системами. Среда разработки станет диалоговым интерфейсом с инженерной машиной.</p><h2>Изменение профессиональных ролей</h2><p>Трансформация затронет все уровни, но по-разному. Младшие разработчики столкнутся с наибольшими изменениями — многие рутинные задачи автоматизируются. Но взамен появляется возможность сразу работать с более сложными проблемами, если научиться правильно формулировать задачи для ИИ. Ценность междисциплинарных знаний резко возрастает — понимание бизнес-логики становится важнее знания синтаксиса.</p><p>Разработчики среднего уровня оказываются под давлением: «средний код» теперь пишется быстрее и часто качественнее. Путь выживания — развитие в сторону архитектуры, DevOps, безопасности. Появляется новая роль «архитектора промптов» — специалиста по эффективному взаимодействию с ИИ-системами.</p><p>Сениоры усиливают позиции. Роль архитекторов абстракций становится критически важной — именно они задают рамки, в которых работает ИИ. Ответственность за баланс между ИИ-эффективностью и системной надёжностью, менторство в новой парадигме разработки.</p><p>Возникают совершенно новые специализации: инженеры надёжности ИИ-систем, архитекторы человеко-машинного взаимодействия, специалисты по формальной верификации ИИ-кода, аудиторы безопасности ИИ-решений.</p><h2>Команды будущего</h2><p>Структура команд кардинально изменится. Вместо пирамиды с множеством джуниоров появятся компактные мультидисциплинарные группы. Системный архитектор задаёт ограничения и инварианты. Доменный эксперт формулирует бизнес-требования. ИИ-инженер оптимизирует взаимодействие с моделями. Инженер надёжности контролирует качество и безопасность. ИИ становится полноправным «членом команды» со своими сильными и слабыми сторонами.</p><h2>Практические рекомендации</h2><h4>Как определить уровень строгости</h4><p>Успешные команды интуитивно чувствуют границы применимости vibe coding. Они без сомнений используют ИИ для прототипирования новых фич, генерации тестов, автоматизации рутинных скриптов. Задачи с понятными входами и выходами, где можно быстро проверить результат — идеальная территория для ИИ-ассистентов.</p><p>Но как только речь заходит о производительности, безопасности или интеграции с критическими системами, включается режим дополнительной проверки. Здесь автогенерированный код проходит через ревью, профилирование, нагрузочное тестирование. Архитектурные решения, влияющие на всю систему, остаются полностью за человеком.</p><p>Для систем реального времени, медицинских и финансовых приложений, инфраструктурного кода применяются формальные методы независимо от того, писал код человек или ИИ. Ставки слишком высоки для экспериментов.</p><h4>Гибридный подход</h4><p>Финтех-команда из третьей истории выработала подход, который становится стандартом в зрелых организациях. Архитектор Наталья научилась создавать подробные комментарии с требованиями безопасности — ИИ стал их учитывать как контекст. Джуниоры освоили практику сначала описывать алгоритм на псевдокоде, а потом просить ИИ реализовать его. Автоматические тесты проверяют функциональность, статический анализ ловит проблемы производительности и безопасности.</p><p>Code review в таких командах изменился кардинально. Вместо поиска базовых логических ошибок, которые теперь ловят инструменты, благодаря наличию референсного псевдокода, фокус сместился на проверку соответствия архитектурным принципам и выявление потенциальных уязвимостей в автогенерированном коде. Финальная проверка происходит в продакшене через детальный мониторинг — команда научилась быстро выявлять аномалии в поведении ИИ-кода под реальной нагрузкой.</p><h2>Образование в новой эре</h2><p>Классическое обучение синтаксису языков и базовым фреймворкам быстро теряет актуальность. Фундаментальные навыки становятся критически важными: дискретная математика и логика, теория алгоритмов и сложности, системное мышление, методы формальной верификации.</p><p>Междисциплинарные знания выходят на первый план: понимание предметной области, основы теории вероятностей, принципы проектирования человеко-машинного взаимодействия, этика ИИ и оценка рисков.</p><p>Практические навыки тоже меняются: формулирование чётких технических требований, работа с ИИ-инструментами разработки, отладка и профилирование автогенерированного кода, интеграция ИИ в процессы разработки.</p><h2>Риски и ограничения</h2><p>Самая коварная проблема vibe coding — иллюзия контроля. Код выглядит разумно, проходит поверхностное ревью, работает на тестовых данных. Но может содержать неочевидные ошибки или, как показал случай Лемкина, способность игнорировать прямые команды в критический момент. ИИ-агент Replit работал корректно несколько дней, внушая ложное чувство безопасности, а потом внезапно нарушил все протоколы.</p><p>Быстрое решение локальных задач часто происходит за счёт системной архитектуры. ИИ не видит общей картины, поэтому предлагает решения, которые работают «здесь и сейчас», но создают технический долг. Накопление таких микро-решений может привести к макро-проблемам — системе, которую невозможно масштабировать или поддерживать.</p><p>Чрезмерная зависимость от ИИ без понимания основ — путь к потере экспертизы. Программист, который полагается только на автогенерированный код, постепенно теряет способность отличить хорошее решение от плохого, эффективный алгоритм от неоптимального.</p><p>Вопросы безопасности заслуживают особого внимания. ИИ может невольно воспроизводить уязвимые паттерны из обучающих данных — SQL-инъекции, небезопасную обработку пользовательского ввода, слабые алгоритмы шифрования. Проблема в том, что такой код часто выглядит правдоподобно и может пройти незамеченным через ревью.</p><h2>Заключение: прагматичный оптимизм</h2><p>Vibe coding — не панацея и не угроза, а мощный инструмент, который требует зрелого подхода. Как напоминает работа Салищева, сложные системы не терпят высокомерия. Фраза «да тут всё и так понятно, зачем математика?» — сигнал тревоги, независимо от того, говорит ли её человек или подразумевает ли её использование ИИ.</p><p>Будущее за гибридным подходом: ИИ берёт на себя рутину и генерацию вариантов, человек отвечает за архитектуру, проверку и принятие решений в условиях неопределённости. Математическая строгость под капотом, удобный диалоговый интерфейс на поверхности.</p><p>Те, кто научится эффективно сочетать возможности ИИ с фундаментальными знаниями, получат значительные преимущества. Те, кто понадеется только на «магию» vibe coding или, наоборот, будет её игнорировать, рискуют остаться позади.</p><p>Эпоха перемен уже началась. Время готовиться — сейчас.</p>]]></content:encoded>
    </item>
    <item>
      <title>Будущее фронтенда: куда движется React, Vue и Angular</title>
      <link>https://tproger.ru/articles/budushhee-frontenda--kuda-dvizhetsya-react--vue-i-angular</link>
      <comments>https://tproger.ru/articles/budushhee-frontenda--kuda-dvizhetsya-react--vue-i-angular?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вадим Егорцев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/budushhee-frontenda--kuda-dvizhetsya-react--vue-i-angular</guid>
      <description><![CDATA[<p>Как изменились React, Vue и Angular за последние 5-10 лет? Эксперты ответили, что будет с фронтенд-разработкой в 2026 году и стоит ли переходить на фреймворки без VDOM и гидратации.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/budushhee-frontenda--kuda-dvizhetsya-react--vue-i-angular">Будущее фронтенда: куда движется React, Vue и Angular</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[Собеседование]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Angular]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 19 Sep 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>За последние пять лет IT-сфера изменилась так сильно, что джун из 2020 года сегодня бы не прошёл собеседование на ту же позицию.</p><p>Если вы фронтенд-разработчик или только планируете им стать, эта публикация поможет вам сориентироваться в текущей ситуации:</p><ul><li>Узнаете, какие крупные изменения произошли в React, Vue и Angular за последние 5-10 лет.</li><li>Поймёте, стоит ли учить новые фреймворки без виртуального DOM или лучше углубиться в проверенные решения.</li><li>Разберётесь, почему компании продолжают требовать знание React, хотя Solid.js работает быстрее.</li></ul><p>Тимлид и разработчики рассказали, как они видят будущее профессии. Объяснили, почему джуну недостаточно знать только JS, какие навыки помогут вам зарабатывать больше и оставаться востребованным специалистом.</p><p>ℹ️ <i>После прочтения вы сможете принять взвешенное решение: продолжать углубляться в текущий стек, осваивать новые инструменты или развивать смежные навыки.</i></p><h2>Как изменились React, Vue и Angular за последние 5-10 лет?</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-09-18/f488b013-32ed-47d0-ada9-3a1c3a84b59f.jpg" alt="" /></figure><h3>React</h3><p>До 2019 года разработчики писали объёмные классы с методами жизненного цикла. Они управляли состоянием через setState и передавали данные через пропсы. Потом появились <b>хуки </b>— с тех пор логику помещают в функции, состоянием управляют через useState и useReducer, побочные эффекты контролируют через useEffect.</p><p>Серверные компоненты решили проблему первой загрузки. Тяжёлые части приложения обрабатывает сервер и отправляет клиенту уже готовыми. Конкурентный режим разбивает отрисовку на части и расставляет приоритеты для обновлений — интерфейс не виснет, когда нагрузка возрастает.</p><h3>Vue</h3><p>Composition API сделал организацию кода более гибкой. Логику теперь группируют по функциональности, а не по типам опций. Ещё реактивность переписали с нуля на прокси-объектах, что ускорило отслеживание изменений.</p><p>Компилятор научился оптимизировать шаблоны на этапе сборки. Телепорты решили проблему с модальными окнами и всплывающими подсказками — компоненты отрисовываются в нужном месте DOM-дерева независимо от родителя.</p><p><a href="https://tproger.ru/articles/v-kakuyu-storonu-razvivaetsya-vue-i-est-li-emu-sovremennye-alternativy">В какую сторону развивается Vue и есть ли ему современные альтернативы</a></p><h3>Angular</h3><p>Каждый компонент сам определяет свои зависимости, поэтому пропала необходимость в модулях. <b>Сигналы</b> заменили зонную детекцию изменений. Теперь Angular точно знает, какие части интерфейса нужно обновлять.</p><p>Строгая типизация и декораторы сделали код более предсказуемым. Встроенные инструменты покрывают все потребности крупных проектов: формы с валидацией, маршрутизация с ленивой загрузкой, HTTP-клиент с перехватчиками.</p><p>Фреймворки уходят от сложных решений к более простым. Тренд последних лет — делать приложения быстрее для пользователей и удобнее для разработчиков.</p><p>Пока большая троица занималась «работой над ошибками», появился запрос на <a href="https://tproger.ru/articles/solidjs-i-qwik--frontend-novogo-pokoleniya">что-то</a> более лёгкое и быстрое.</p><h2>Фронтенд-фреймворки кажутся избыточными — возвращаемся к ванильному JS?</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-09-18/1af1ebd0-69e3-4f07-8d6d-053e8c3e70bb.jpg" alt="" /></figure><blockquote>Тот, кто попробовал фреймворки, никогда не вернется к чистому ванильному JS 🙂 Меняются и развиваются сами фреймворки, но тренда на отказ от них точно нет.</blockquote><p>Несмотря на тенденцию упрощения, разработчики сходятся во мнении, что полного отказа от фреймворков не произойдёт.</p><blockquote>В моде решения на основе концепции «исчезающих фреймворков», которые на выходе выдают почти чистый императивный JS-код. Дело в том, что это общий тренд в IT, где вся сложность и расчёты уходят в инфраструктуру, собирая минимальный бандл, где нет ничего лишнего для клиентов.</blockquote><p>Фреймворки — это ещё и способ стандартизации разработки в командах.</p><blockquote>Фреймворки никогда не будут избыточными. Один из первых вопросов на собеседовании —  инструмент, с которым ты умеешь работать. Больший пласт знаний сегодня — это фреймворк. Остальное, чаще всего, бизнес-логика того или иного проекта.</blockquote><p>Из-за критики традиционных фреймворков появился новый класс решений. Больше всего ругают VDOM и гидратацию, хотя они считались неотъемлемой частью современных SPA.</p><h2>Стоит ли переходить на фреймворки без VDOM и гидратации?</h2><p>Solid.js и Svelte уже несколько лет развивают концепцию тонкой реактивности. В 2021 году появился Qwik, который вообще отказался от гидратации.</p><blockquote>SolidJS хорош для аналогов десктопных приложений в браузере — Figma, Miro. Здесь приложение загружается один раз, а потом работает долго и должно быть максимально отзывчивым. Qwik отлично подойдёт, если вы создаёте сайт, куда пользователь приходит за контентом, и важно показать ему этот контент мгновенно.</blockquote><p>При этом массового перехода на новые фреймворки пока не происходит. Компании продолжают искать React и Vue разработчиков — проекты на Solid.js и Qwik остаются экспериментальными.</p><blockquote>Сами по себе фреймворки мало значат для коммерческой разработки без сторонних библиотек вокруг них. Вот когда критическая масса разработчиков популярных библиотек мигрирует на Qwik, а вместе с ними и сообщество, что-то сдвигается.</blockquote><p>Проблема новых фреймворков — это отсутствие экосистемы. React и Vue окружены тысячами готовых библиотек для любых задач. На Solid.js и Qwik большинство решений придётся писать самостоятельно.</p><blockquote>Без поддержки сообщества пользователей ни один инструмент не сможет стать массовым. Даже если работаешь в заказной разработке и есть возможность расширить кругозор команды, сомнительно брать на тест инструмент, под который придётся писать до 30% базового функционала.</blockquote><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-09-18/74e9b111-b93b-4fdf-a8b5-9db67bf7e95e.jpg" alt="" /></figure><p>Команды, которые захотят перейти на новые фреймворки, столкнутся с дополнительными сложностями. Разработчикам придётся учить новые концепции и подходы, а компаниям — тратить время и деньги на переобучение.</p><blockquote>У новых фреймворков высокий порог входа: вас ждёт переобучение команды и сложности ручной оптимизации.</blockquote><p>Технологии развиваются циклично. То, что сейчас кажется инновацией, переосмысливает старые подходы.</p><blockquote>Во времена DDR/DDR2 было сложно выполнять клиентский рендеринг, а вот серверам ресурсов хватало. Разработчики как могли воплощали подход, который сейчас называется SSR, и возвращали на клиент готовую разметку для компонентов. И никакого тебе VDOM.</blockquote><p>Пока новые фреймворки остаются нишевыми инструментами. React, Vue, Angular продолжат доминировать в коммерческой разработке за счёт экосистемы и сообщества.</p><h2>Как изменится роль фронтенд-разработчика в 2026 году?</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-09-18/6027e94f-65a4-4ab2-9364-6f5e322267f1.jpg" alt="" /></figure><p>В 2020 году джуниор находил работу, если знал HTML, CSS и основы JS. Сегодня работодатели требуют от новичков React или Vue, TypeScript, системы сборки — и это не конец списка. Ещё и вакансий на всех начинающих не хватает, компании сразу ищут мидлов.</p><p>Инструменты с искусственным интеллектом — Copilot, ChatGPT, Cursor — ускоряют работу. Они же повышают планку: если джун с помощью ИИ работает как мидл, то мидл должен демонстрировать более глубокие знания. Работодатели стали больше ценить тех, кто понимает принципы работы кода, а не просто копирует готовые решения.</p><blockquote>Зная JavaScript и любой инструмент даже не из большой тройки, с использованием AI можно быстро погрузиться в другой нужный инструмент.</blockquote><p>Чёткой границы между фронтендом и бэкендом больше нет. От фронтенд-разработчика ждут, что тот разбирается в серверной части, умеет настраивать процессы непрерывной интеграции и доставки, работает с базами данных.</p><h2>Какие навыки развивать разработчику, чтобы оставаться востребованным?</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-09-18/d62a3715-b713-4af3-8658-3ff4a6df7d85.jpg" alt="" /></figure><p>Технические навыки — это основа. Когда понимаете алгоритмы и структуры данных, вы пишете быстрый код. Когда знаете паттерны проектирования, ваши приложения легко поддерживать.</p><p><b>Учитесь быстро разбираться в новых инструментах вместо того, чтобы зацикливаться на одном фреймворке</b>.</p><blockquote>По мне так лучше быть фреймворк-агностиком, нежели фанатом одного, которого легко чем-то обидеть.</blockquote><p>Архитектурное мышление отличает <b>«</b>сильного<b>»</b> разработчика:</p><ul><li>Как организовать код, чтобы его легко поддерживать?</li><li>Как спроектировать API, чтобы оно не ломалось при изменениях?</li><li>Как построить систему, которая выдержит рост нагрузки?</li></ul><p>Те, кто решают эти вопросы, получают офферы с космическими зарплатами.</p><p><b>Софт-скиллы</b> тоже влияют на карьерный рост:</p><ul><li>Объясните техническую проблему менеджеру простыми словами.</li><li>Проводите код-ревью так, чтобы не обидеть коллегу, но улучшить код.</li><li>Оценивайте сроки и управляйте ожиданиями.</li><li>Аргументируйте выбор технологии.</li></ul><p>Когда вы понимаете бизнес, вы становитесь партнёром. Почему мы делаем эту фичу? Как она повлияет на метрики? Какие есть альтернативы? Разработчик, который мыслит категориями бизнеса, становится незаменимым.</p><p>Рынок вынуждает постоянно учиться, потому что индустрия меняется каждый год. Кто застревает в зоне комфорта — отстаёт.</p><p><i>Как ChatGPT повлиял на ценность вашего труда? Успеваете подстраиваться под требования рынка, параллельно работать и пробовать новые технологии? Делитесь в комментариях.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Квиз: Сможешь ли ты сделать устойчивую систему на Java?</title>
      <link>https://tproger.ru/quiz/kviz--smozhew-li-ty-sdelat-ustojchivuyu-sistemu-na-java-</link>
      <comments>https://tproger.ru/quiz/kviz--smozhew-li-ty-sdelat-ustojchivuyu-sistemu-na-java-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/quiz/kviz--smozhew-li-ty-sdelat-ustojchivuyu-sistemu-na-java-</guid>
      <description><![CDATA[<p>Тебе нужно разработать систему, которая будет выдерживать любую нагрузку и не падать. Ты разработчик на Java, и твоя задача — выбрать самый подходящий фреймворк. Пройди квиз и сделай эту систему «железобетонной»!</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/quiz/kviz--smozhew-li-ty-sdelat-ustojchivuyu-sistemu-na-java-">Квиз: Сможешь ли ты сделать устойчивую систему на Java?</a>»</p>]]></description>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Викторины]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 04 Mar 2025 07:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Ты только что пришел в топовую компанию, и тебе сразу же упала задача уровня хард — с нуля написать устойчивую систему, которая останется надежной даже в самых экстремальных условиях. В твоем распоряжении Java и его фреймворки — докажи боссу, что ты крутой прогер!</p>]]></content:encoded>
    </item>
    <item>
      <title>Как проектировать интерфейсы для мобилок: подробный гайд</title>
      <link>https://tproger.ru/articles/principy-proektirovaniya-interfejsov-dlya-mobilnyh-prilozhenij</link>
      <comments>https://tproger.ru/articles/principy-proektirovaniya-interfejsov-dlya-mobilnyh-prilozhenij?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/principy-proektirovaniya-interfejsov-dlya-mobilnyh-prilozhenij</guid>
      <description><![CDATA[<p> Как спроектировать интерфейс мобильного приложения. Показываем основные нюансы, на которые стоит обратить внимание. Рассматриваем пошаговую инструкцию ✔ Tproger
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/principy-proektirovaniya-interfejsov-dlya-mobilnyh-prilozhenij">Как проектировать интерфейсы для мобилок: подробный гайд</a>»</p>]]></description>
      <category><![CDATA[Мобильная разработка]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Дизайн интерфейсов и UX]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 09 Dec 2024 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Проектирование мобильных приложений — это сложный процесс, требующий сочетания эстетики, функциональности и удобства использования. Здесь важно учитывать следующие  аспекты — интуитивно понятная навигация, визуальная иерархия, адаптивность, минимализм и доступность. Сегодня рассмотрим основные принципы создания интерфейсов, которые обеспечивают высокий уровень юзабилити и улучшают пользовательский опыт.</p><h2>Принципы удобства использования (Usability) в мобильных приложениях</h2><p>Удобство использования или юзабилити — ключевой элемент успешного проектирования мобильных приложений. Этот принцип нацелен на понятное, простое и приятное взаимодействие пользователей с интерфейсом приложения.</p><h3>Что такое удобство использования и почему это важно?</h3><p>Удобство использования — это показатель того, насколько легко и эффективно пользователь может выполнять задачи в приложении. Оно играет решающую роль в удержании аудитории: даже функциональное приложение может быть отвергнуто, если его интерфейс неудобен. Как понять, что все правильно?</p><ul><li>Требуется больше времени на взаимодействие с приложением;</li><li>Снижается количество ошибок пользователей;</li><li>Повышается лояльность благодаря положительному опыту.</li></ul><h3>Интуитивно понятная навигация и структура приложения</h3><p>Интуитивность — когда пользователю не нужно учиться работать с приложением. Ключевые элементы:</p><ul><li><b>Простая структура. </b>Главное меню должно быть минималистичным, с четким выделением основных функций;</li><li><b>Ожидаемое поведение интерфейса.</b> Элементы вроде кнопок или ссылок должны выглядеть так, как предполагает пользователь;</li><li><b>Видимость пути.</b> Всегда важно показывать, где находится пользователь и как он может вернуться к началу.</li></ul><p>Пример хорошей навигации — использование нижней панели с доступом к ключевым разделам приложения. Плохая навигация — вложенные меню, где нужно нажать на кучу кнопок, чтобы, например, вернуться к началу.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-12-03/3e8cb6c1-0145-42a2-8031-9731cb4d0669.png" alt="" /><figcaption>Хорошая навигация на Spotify</figcaption></figure><h3>Упрощение взаимодействия с учётом ограниченного экрана</h3><p>Маленькие экраны мобильных устройств требуют продуманного подхода. Что учитываем:</p><ul><li>Минимум информации на одном экране. Разделение задач на этапы делает взаимодействие удобным;</li><li>Используем жесты (например, свайпы) для экономии пространства.</li><li>Делаем контрастный и четкий дизайн, адаптированный для восприятия на малых экранах.</li></ul><h3>Примеры хорошей и плохой навигации в мобильных интерфейсах</h3><p><b>Примеры хорошей навигации:</b></p><ul><li>Приложения для путешествий (например, карты), в которых все ключевые функции видны сразу.</li><li>Маркетплейсы, где есть кнопки быстрого доступа к категориям.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-12-03/67915ec3-538e-4230-85bf-412728957a12.png" alt="" /><figcaption>Так выглядят карты от Apple</figcaption></figure><p><b>Примеры плохой навигации:</b></p><ul><li>Приложения, где ключевые элементы «спрятаны» в дополнительных меню (например, цена на продукт или вкладка для скачивания)</li><li>Непонятные иконки без подписей, из-за которых пользователь теряется (так было, например, в ранних версиях Snapchat).</li></ul><p>Принципы юзабилити при проектировании мобильных приложений — это не только создание привлекательного дизайна, но и забота о функциональности и комфорте взаимодействия. Продуманная структура, простота и адаптация к ограничениям экранов формируют положительный пользовательский опыт, который выделит приложение на фоне других.</p><h2>Адаптивность и отзывчивость интерфейса</h2><p>Адаптивность и отзывчивость интерфейса — неотъемлемые принципы проектирования мобильных приложений. Рассмотрим основные элементы.</p><h3>Поддержка различных размеров экранов и ориентаций устройств</h3><p>Мобильные устройства отличаются разнообразием экранов: от компактных смартфонов до больших планшетов. Чтобы обеспечить универсальность пользовательского интерфейса, важно учитывать:</p><ul><li><b>Автоматическую адаптацию к ориентации устройства.</b> Горизонтальная и вертикальная ориентации должны одинаково обеспечивать удобный доступ к функциям;</li><li><b>Динамическое масштабирование.</b> Интерфейс приложения должен корректно отображаться как на компактных, так и на больших экранах.</li></ul><h3>Использование адаптивных элементов интерфейса и сетки</h3><p>Адаптивные элементы и сетки делают интерфейс мобильных приложений гибким и удобным. Основные рекомендации:</p><ul><li><b>Сетки. </b>Использование 12-колоночной системы сеток помогает выровнять элементы и адаптировать их к разным разрешениям экрана;</li><li><b>Элементы с пропорциональными размерами.</b> Кнопки, текстовые поля и изображения должны плавно масштабироваться.</li></ul><p>Использование гибкой сетки улучшает юзабилити, так как интерфейс выглядит логично и привлекательно.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-12-03/67646095-04ed-4b1a-aed7-dfd1c97b26f8.png" alt="" /><figcaption>Вот как выглядит Ozon на десктопе благодаря сетке. Безусловно, мы тут про мобильные приложения говорим, но принцип понятен</figcaption></figure><h3>Советы по созданию отзывчивого дизайна с помощью автолейаутов и flexbox</h3><p>Такие инструменты, как автолейаут и flexbox, упрощают процесс создания адаптивного дизайна.</p><ul><li>Flexbox. С его помощью элементы автоматически перестраиваются, занимая оптимальное место на экране;</li><li>Автолейауты. Ускоряют размещение элементов интерфейса, особенно в условиях ограниченного пространства.</li></ul><p>Эти подходы помогают сделать приложения удобными для всех типов устройств, улучшая их визуальное восприятие и функциональность.</p><h3>Как обеспечить хорошую читаемость текста и удобное размещение элементов</h3><p>Для читаемости и удобства взаимодействия важны:</p><ul><li><b>Размеры шрифта.</b> Минимальный размер текста — 14 пикселей;</li><li><b>Контрастность. </b>Текст и фон должны быть легко различимыми;</li><li><b>Расстояние между элементами.</b> Клавиши и другие интерактивные зоны не должны располагаться слишком близко друг к другу (на языке мемов — «коллеги, нужно хочется побольше воздуха»).</li></ul><h2>Принципы визуальной иерархии</h2><p>Эффективный пользовательский интерфейс должен не только выглядеть привлекательно, но и помогать юзеру быстро находить нужную информацию. Принципы визуальной иерархии играют ключевую роль в проектировании мобильных интерфейсов, направляя внимание пользователя в нужное русло и упрощая взаимодействие.</p><h3>Использование размеров, цветов и контрастов для выделения важной информации</h3><p>Размеры, цвета и контрастность позволяют расставить акценты в интерфейсе мобильных приложений:</p><ul><li><b>Размеры.</b> Более крупные элементы привлекают внимание;</li><li><b>Цвета.</b> Использование ярких оттенков для ключевых элементов (например, кнопок действий) помогает пользователю легко их идентифицировать;</li><li><b>Контраст.</b> Четкий контраст между текстом и фоном улучшает читаемость и повышает удобство использования.</li></ul><h3>Создание иерархии элементов для привлечения внимания пользователя</h3><p>Размещение элементов на экране должно учитывать логику взгляда пользователя. Основные принципы:</p><ul><li><b>F-образный паттерн.</b> Пользователи читают экраны по горизонтали сверху вниз, что следует учитывать при размещении информации;</li><li><b>Визуальные маркеры.</b> Иконки, стрелки и выделения направляют внимание на важные действия.</li></ul><p>Эффективная иерархия помогает избежать перегрузки информацией, делая интерфейс более удобным.</p><h3>Баланс между текстом и визуальными элементами</h3><p>Сбалансированное использование текста и графики делает мобильные приложения понятными и эстетичными:</p><ul><li><b>Минимализм.</b> Четкие заголовки и лаконичные описания помогают удерживать внимание;</li><li><b>Графика.</b> Иллюстрации или иконки должны дополнять, а не заменять текстовую информацию;</li><li><b>Соотношение.</b> Не менее 40% пространства должно быть отведено под «воздух», чтобы элементы не выглядели сжатыми.</li></ul><h3>Примеры эффективного использования визуальной иерархии в мобильных приложениях</h3><p>Хороший пример: в приложении для заказа еды крупные фотографии блюд сопровождаются яркими кнопками «Добавить в корзину». Контраст помогает пользователю быстро сориентироваться.</p><p>Плохой пример: сложные таблицы без визуального разделения информации. Это снижает читабельность и затрудняет восприятие.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-12-03/4e7997e9-bb14-4501-83e5-d31f4b3cb10d.png" alt="" /><figcaption>Сгенерировано нейросетью</figcaption></figure><p>Визуальная иерархия — один из основных принципов проектирования мобильных интерфейсов. Грамотное использование размеров, цветов и баланса между текстом и визуальными элементами улучшает опыт взаимодействия пользователя с приложением.</p><h2>Обратная связь и взаимодействие</h2><p>Важный аспект проектирования мобильных интерфейсов — организация качественной обратной связи между пользователем и приложением. Это усиливает восприятие, улучшает опыт взаимодействия и повышает уровень юзабилити. Рассмотрим основные принципы.</p><h3>Предоставление визуальной и тактильной обратной связи при взаимодействии</h3><p>Пользователи ожидают мгновенной реакции приложения на любое действие, будь то нажатие кнопки или свайп. Важные элементы:</p><ul><li><b>Визуальная обратная связь.</b> Изменение цвета кнопок, всплывающие уведомления или выделение активных элементов сигнализируют об успешном действии;</li><li><b>Тактильная обратная связь.</b> Легкие вибрации при взаимодействии создают эффект физического присутствия и делают процесс интуитивно понятным. Самый банальный пример — 3D Touch на устройствах Apple, когда, например, ссылка в Safari открывается с вибрацией в новом окне, то же самое с фотографиями в пленке.</li></ul><h3>Использование анимаций и переходов для улучшения пользовательского опыта</h3><p>Анимации могут не только украшать интерфейс мобильных приложений, но и облегчать его восприятие:</p><ul><li><b>Плавные переходы.</b> Анимации помогают визуально объяснить, что происходит: например, как открывается меню или перемещается элемент;</li><li><b>Интерактивные элементы.</b> Кнопки с эффектом нажатия или иконки, которые анимируются при взаимодействии, делают приложение более живым.</li></ul><p>Важно соблюдать баланс: чрезмерное количество анимаций перегружает интерфейс и снижает производительность.</p><h3>Как сделать интерфейс отзывчивым и живым, не перегружая его анимацией</h3><p>Основной принцип — минимализм:</p><ul><li>Используйте анимацию для подсказок и подтверждений. Так, плавное появление сообщения об ошибке делает интерфейс более дружелюбным;</li><li>Адаптируйте анимации для разных устройств, чтобы сохранять производительность.</li></ul><p>Совет: избегайте сложных 3D-эффектов и длительных анимаций, которые могут отвлекать от основного действия.</p><h3>Примеры хорошей обратной связи в популярных приложениях</h3><p>Положительный пример: в приложении для заметок Google Keep кнопка «Добавить» анимируется, сигнализируя о том, что запись сохранена. Это интуитивно понятно и просто.</p><p>Отрицательный пример: в приложениях с перегруженными анимациями и задержкой реакции (например, медленно открывающиеся меню) пользователь теряет концентрацию.</p><p>Обратная связь — одна из основных характеристик качественного пользовательского интерфейса. Сбалансированное использование визуальных эффектов, тактильной реакции и анимаций делает мобильные приложения более удобными и отзывчивыми, улучшая общее восприятие и опыт пользователя.</p><h2>Минимализм и фокус на контенте</h2><p>Минимализм — ключевой принцип при проектировании мобильных интерфейсов, который способствует улучшению юзабилити и снижению когнитивной нагрузки на пользователя. Рассмотрим основные аспекты минималистичного подхода.</p><h3>Принцип «чем меньше, тем лучше»: избегайте избыточного функционала и информации</h3><p>В мире мобильных приложений избыточность отвлекает пользователя от главной цели. Оптимизация предполагает:</p><ul><li><b>Отказ от избыточных функций. </b>Приложение должно предлагать только те функции, которые необходимы для выполнения основных задач;</li><li><b>Упрощение визуального контента.</b> Минимум текста и графики помогает пользователю быстрее ориентироваться.</li></ul><p>Пример: приложение для концентрации внимания Forest, в котором основное внимание уделяется одной функции — вырастить дерево, не прикасаясь к телефону на протяжении определенного количества минут. Простая навигация и отсутствие лишних элементов позволяют сосредоточиться на главной цели.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-12-03/e149e11b-debb-4168-abeb-806a46260644.png" alt="" /></figure><h3>Минимизация элементов интерфейса для сохранения фокуса на контенте</h3><p>Интерфейс мобильных приложений должен подчеркивать контент, а не доминировать над ним. Это достигается за счет:</p><ul><li>Простой структуры экранов с акцентом на содержимое;</li><li>Использования цветовых акцентов для выделения ключевых действий;</li><li>Минимизации отвлекающих элементов: сложных иконок, избыточных меню, всплывающих окон.</li></ul><p>Совет: оставьте только те элементы, которые действительно необходимы для взаимодействия. Например, в Instagram* вся структура построена вокруг контента: фотографии и видео остаются в центре внимания, а, например, мессенджер выведен в отдельную вкладку и не отображается в главном меню снизу.</p><p><i>*Корпорация Meta признана экстремистской в РФ</i></p><h3>Уменьшение когнитивной нагрузки на пользователя</h3><p>Пользовательский опыт становится приятнее, если приложение избавляет от необходимости запоминать сложные маршруты или действия. Для этого:</p><ul><li>Убирайте ненужные этапы взаимодействия;</li><li>Делайте навигацию интуитивно понятной;</li><li>Используйте лаконичные инструкции и понятные иконки.</li></ul><h3>Примеры минималистичного подхода в мобильных приложениях</h3><p>Хороший пример: Тот же Google Keep, где интерфейс полностью сосредоточен на создании и хранении заметок. Никаких лишних настроек или сложных функций.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-12-03/b8af05d0-d824-4925-bdd1-1af5fd239fb6.png" alt="" /></figure><p>Плохой пример: приложения с избыточной анимацией или всплывающими окнами, которые отвлекают от контента.</p><p>Минималистичный подход к проектированию мобильных интерфейсов помогает пользователю сосредоточиться на выполнении основных задач и улучшает общее восприятие. Принципы минимализма — это отказ от лишнего, простота и удобство, что делает приложения более понятными и функциональными.</p><h2>Доступность (Accessibility)</h2><p>Доступность интерфейсов в мобильных приложениях — это не только забота о пользователях, но и важный фактор, повышающий юзабилити. Разработка доступных приложений расширяет аудиторию и делает взаимодействие удобным для всех, включая людей с ограниченными возможностями.</p><h2>Обеспечение доступности интерфейса для пользователей с ограниченными возможностями</h2><p>Чтобы интерфейс мобильных приложений был доступным, необходимо учитывать потребности людей с различными нарушениями, в том числе:</p><ul><li>Зрение (частичная или полная потеря, дальтонизм);</li><li>Слух;</li><li>Опорно-двигательный аппарат (ограниченные возможности взаимодействия с экраном);</li><li>Когнитивные особенности (дислексия, проблемы с вниманием).</li></ul><h2>Использование контрастных цветов, доступных шрифтов и альтернативных текстов</h2><p>Контрастные цветовые схемы и крупные, легко читаемые шрифты облегчают взаимодействие. Основные советы:</p><ul><li>Используйте шрифты без засечек (sans-serif), по типу Arial или Roboto;</li><li>Выбирайте контрастные сочетания текста и фона, которые соответствуют стандартам WCAG (например, чёрный текст на белом фоне);</li><li>Добавляйте альтернативные тексты к изображениям для работы с экранными дикторами. Это особенно важно для визуальных приложений.</li></ul><p>Пример: в Uber значки и шрифты адаптированы для режима высокой контрастности, что делает их удобными для пользователей с нарушениями зрения.</p><h2>Важность поддержки экранных дикторов и жестов</h2><p>Современные интерфейсы мобильных приложений должны быть оптимизированы для работы с технологиями вспомогательного ввода, такими как:</p><ul><li>Экранные дикторы (например, VoiceOver на iOS или TalkBack на Android);</li><li>Управление жестами, позволяющее заменить физические кнопки.</li></ul><p>Для этого важно структурировать интерфейс так, чтобы дикторы правильно озвучивали элементы: кнопки, заголовки, поля ввода.</p><h2>Как улучшить доступность с минимальными усилиями</h2><p>Достичь высокой доступности можно даже с небольшими изменениями:</p><ol><li>Используйте встроенные библиотеки платформ для включения функций доступности;</li><li>Проверяйте интерфейс с помощью симуляторов (например, Google Accessibility Scanner);</li><li>Стремитесь к простоте и интуитивности, чтобы сократить время на адаптацию.</li></ol><p>Основной принцип доступности — обеспечение равных возможностей для всех пользователей. Удобный и доступный пользовательский интерфейс не только улучшает опыт, но и делает мобильные интерфейсы соответствующими современным стандартам.</p>]]></content:encoded>
    </item>
    <item>
      <title>Паттерн CQRS — руководство для чайников</title>
      <link>https://tproger.ru/articles/cqrs-dlya-chajnikov</link>
      <comments>https://tproger.ru/articles/cqrs-dlya-chajnikov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Nikita Gerasimov]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/cqrs-dlya-chajnikov</guid>
      <description><![CDATA[<p>Рассказываем, что такое паттерн CQRS (Command Query Responsibility Segregation), зачем он нужен и как внедрить его для своего проекта.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/cqrs-dlya-chajnikov">Паттерн CQRS — руководство для чайников</a>»</p>]]></description>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 23 May 2023 11:55:55 GMT</pubDate>
      <content:encoded><![CDATA[<p>Не так давно в рамках одного из проектов впервые столкнулся с таким понятием, как CQRS. Честно скажу, заинтересовало сразу, потому что в проект очень удобно и просто встроиться, легко понять, что, где и как происходит. Достаточно прочитать одну статью или просмотреть обучающее видео и ты уже “вооружен”, чтобы приступать к работе на проекте.</p><p>И сейчас, спустя время, хочу поделиться с читателями издания Tproger своим видением построения проекта по этому паттерну. Начнем с небольшой теории.</p><p>Паттерн CQRS (Command Query Responsibility Segregation) – это подход к проектированию системы, который разделяет операции чтения и записи данных на две отдельные модели. Этот подход позволяет улучшить производительность системы и упростить ее сопровождение. Часто на просторах интернета вы можете встретить подобную схему.</p><figure><img src="https://media.tproger.ru/uploads/2023/05/f4d18e67-e035-42af-b4d2-f613c3a814f5.png" alt="" /></figure><p>Как было сказано, CQRS разделяет операции над данными на две категории: <b>команды</b>, которые вносят изменения в состояние системы и <b>запросы</b> – операции получения данных, без внесения изменений в состояние.</p><p>Проще всего это объяснить на примере стандартных CRUD операций. В CQRS операция чтения (Read) будет являться <b>запросом</b>, т.к. с помощью нее получаются данные и ничего более. Остальные же операции (Create, Update, Delete) в данном подходе будут являться <b>командами</b>, которые так или иначе изменяют состояние.</p><h2>Почему CQRS</h2><p>Кратко пройдемся по преимуществам данного подхода:</p><ul><li>Простота понимания: разделение операций чтения и записи позволяет создавать более чистый и модульный код.</li><li>Улучшенная производительность: разделение операций чтения и записи позволяет оптимизировать каждую из них для конкретных задач.</li><li>Улучшенная масштабируемость: разделение операций чтения и записи позволяет легко масштабировать каждую из них отдельно.</li><li>Простота тестирования: разделение операций чтения и записи позволяет легко тестировать каждую из них отдельно.</li></ul><p>Теперь, разобравшись в теории, что такое CQRS и зачем он нужен, предлагаю перейти к непосредственной практике.</p><h2>Подготовка проекта</h2><p>В рамках статьи разберем простейший пример приложения с применением паттерна CQRS. Для этого создадим ASP.NET Core Web API проект на .NET 6.0</p><figure><img src="https://media.tproger.ru/uploads/2023/05/7aa5ec1a-37be-4b5b-aff8-d14dfefe3763.jpg" alt="" /></figure><p>В данном примере прибегну к помощи библиотеки MediatR, поэтому добавлю ее в самом начале.</p><figure><img src="https://media.tproger.ru/uploads/2023/05/d5c5484e-185a-4d59-936f-ea7389c63b44.jpg" alt="" /></figure><p>И после этого, согласно документации библиотеки, зарегистрируем ее в контейнере зависимостей.</p><p>Перед тем, как приступить к прописыванию логики, создадим класс, описывающий объект товара, назовем его “<b>Product</b>” и именно над объектами данного класса будем производить необходимые операции.</p><p>Далее создадим некое подобие контекста базы данных (или репозитория), в котором инкапсулируем работу с объектами класса “<b>Product</b>”. В нашем случае пропишем в этом классе методы для получения списка данных и добавления элемента в этот список.</p><p>После этого необходимо зарегистрировать наше хранилище.</p><h2>Запрос для получения данных</h2><p>Теперь можно приступить к написанию первых запросов и команд, которые наглядным образом покажут принцип работы с данными в этом подходе. Для начала создадим 3 папки в корне проекта “<b>Queries</b>”, “<b>Commands</b>” и “<b>Handlers</b>”.</p><p>Начнем с базового – запроса для получения списка всех продуктов из нашего хранилища. Для этого в папку “<b>Queries</b>” добавим record, который назовем “<b>GetProductsQuery</b>”, он будет реализовывать интерфейс “<b>IRequest</b>” из пространства имен “<b>MediatR</b>”, передав в этот интерфейс параметр, который будет указывать на тип возвращаемых данных при выполнении запроса – в данном случае это “<b>IEnumerable</b>”. В конечном варианте это будет выглядеть следующим образом:</p><p>Далее необходимо создать класс-обработчик указанного запроса. Для этого в папку “Handlers” добавим класс “<b>GetProductsQueryHandler</b>”. Данный класс должен реализовывать“<b>IRequestHandler</b>”, где: TCommand – команда, обработчиком которой будет являться описываемый класс (в нашем случае – это “<b>GetProductsQuery</b>”), а TResponse – тип возвращаемого значения данной команды (тот же параметр, который передавали выше интерфейсу IRequest – “<b>IEnumerable</b>”).</p><p>В данном обработчике будет прописана логика получения данных из нашего хранилища, т.е. можно грубо провести аналогию с обычным сервисом, который “общается” с репозиторями для получения данных. Для этого необходимо получить экземпляр хранилища, с которым будет вестись работа. Создадим приватное свойство только для чтения типа “<b>ProductStore</b>” и инициализируем его в конструкторе:</p><p>Теперь, реализуя интерфейс “<b>IRequestHandler</b>”, создаем метод Handle</p><p>В общем виде класс будет выглядеть следующим образом:</p><p>Далее пропишем контроллер “<b>ProductsController</b>”. Для отправки команд будем использовать интерфейс “<b>IMediator</b>”, а конкретно его метод Send(), который принимает параметром объект команды. Контроллер с методом для получения списка продуктов будет выглядеть следующим образом:</p><p>Запустив приложение можно протестировать работу данного метода через Postman.</p><figure><img src="https://media.tproger.ru/uploads/2023/05/7d0cd0f8-f38d-45e3-ab42-87159c6445a1.jpg" alt="" /></figure><p>Как видим, все работает прекрасно. Теперь можно приступить к написанию команд – операций по изменению данных в хранилище.</p><h2>Команда для добавления данных в хранилище</h2><p>Создадим команду для добавления продукта в наше хранилище. Принцип остается тем же: создаем команду, создаем обработчик команды и добавляем метод в наш контроллер.</p><p>Отличие команды от запроса заключается в том, что необходимо добавить входной параметр – объект, который будем добавлять, а тип возвращаемого значения мы укажем “<b>Product</b>”, чтобы вернуть новый объект.</p><p>Итак, собственно, сама команда:</p><p>Обработчик команды по своей структуре абсолютно идентичен обработчику запроса. Единственное, на что стоит обратить внимание – это метод “<b>Handle</b>”. В нем мы берем из хранилища идентификатор крайнего элемента, чтобы дать новому объекту подходящий ID. Наименование товара берем из входного параметра request, который является экземпляром команды “<b>AddProductCommand</b>”. После записи в хранилище, созданный экземпляр с новым ID возвращаем пользователю.</p><p>Для того, чтобы вернуть пользователю созданный объект, опишем еще один запрос для получения элемента по его идентификатору.</p><p>И добавим необходимые методы в наш контроллер. Первый – HttpGet метод для получения объекта по id, который получаем из строки запроса, также этому методу задаем параметр “Name” для того, чтобы после создания объекта в HttpPost методе по этому параметру переадресовать клиента для получения созданного продукта.</p><p>В HttpPost методе стоит обратить внимание на последнюю строку. В ней вызывается метод “<b>CreatedAtAction</b>”, который используется для создания ответа HTTP 201 Created, который содержит ссылку на вновь созданный ресурс. Этот метод принимает три параметра: имя действия, параметры запроса и объект, который будет возвращен в качестве результата действия.</p><p>Теперь проверим, как это работает с помощью того же Postman.</p><figure><img src="https://media.tproger.ru/uploads/2023/05/8bfa1cb8-4ab1-4eb6-9de9-e1b9f39decfb.jpg" alt="" /></figure><p>Объект был успешно создан и возвращен с новым id. Код ответа – 201 Created и если посмотрим заголовки ответа, то увидим созданный методом CreatedAtAction заголовок Location.</p><figure><img src="https://media.tproger.ru/uploads/2023/05/0584671c-550d-4ccb-a668-3bf1f4a0e3a2.jpg" alt="" /></figure><p>Таким образом, на примере разобрали принцип построения проекта по паттерну CQRS. Данный пример довольно простой и в реальных проектах все намного сложнее, не все разработчики предпочитают использовать библиотеку MediatR, т.к. это замедляет процесс обработки операций, но это уже тема для другой статьи. Здесь же вы могли увидеть наиболее простой для понимания проект, построенный по принципам CQRS.</p><h2>Итог</h2><p>Если кратко подытожить, то стоит отметить следующее:</p><ul><li>CQRS – это подход к проектированию, который разделяет операции над данными на две категории: запросы и команды.</li><li>Запросы – это операции получения данных, не изменяющие состояние.</li><li>Команды – это операции изменения состояния системы.</li><li>Класс (или record), описывающий операцию (будь то команда или запрос), должен имплементировать интерфейс IRequest, где T – тип возвращаемого значения операции. Поля этого класса (или record’a) – входные параметры операции.</li><li>Класс-обработчик операции должен реализовывать интерфейс IRequestHandler, где TCommand – команда, обработчиком которой является данный класс, TResponse – тип возвращаемого значения. Для работы с данными традиционно в этом классе присутствует свойство, представляющее объект репозитория, с которым работает обработчик, этот объект инициализируется в конструкторе, куда он приходит из контейнера зависимостей.</li><li>Основная работа с данными производится в методе Handle(TCommand command, CancellationToken token), типом возвращаемого значения которого является Task.</li><li>Вызов операции из контроллера производится методом Send интерфейса IMediator (объект которого нужно получить из контейнера зависимостей). В качестве параметра в метод Send передается объект класса (или record’a), описывающего операцию.</li></ul><p>В заключении хочется сказать, что паттерн CQRS является одним из наиболее эффективных подходов к проектированию системы, который позволяет улучшить ее производительность и упростить сопровождение. Если вы хотите создать эффективную и легко сопровождаемую систему, то рассмотрите возможность использования паттерна CQRS.</p>]]></content:encoded>
    </item>
    <item>
      <title>Видео: Необычный Python. Паттерны, продолжение. Урок 5</title>
      <link>https://tproger.ru/video/video-neobychnyj-python-patterny-prodolzhenie-urok-5</link>
      <comments>https://tproger.ru/video/video-neobychnyj-python-patterny-prodolzhenie-urok-5?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[theartofdevel]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/video/video-neobychnyj-python-patterny-prodolzhenie-urok-5</guid>
      <description><![CDATA[<p>Пятый урок видеокурса по Python посвящён каталогам и классификации паттернов AbstractFactory, Strategy и Proxy, а также тому, как они появляются.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/video/video-neobychnyj-python-patterny-prodolzhenie-urok-5">Видео: Необычный Python. Паттерны, продолжение. Урок 5</a>»</p>]]></description>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Пост пользователя]]></category>
      <category><![CDATA[Видео]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 09 Jun 2021 03:51:11 GMT</pubDate>
      <content:encoded><![CDATA[<p>В этом видео мы продолжим рассматривать паттерны. Теперь это будут AbstractFactory, Strategy и Proxy.  Я расскажу, как они появляются, как классифицируются и какие каталоги бывают.</p><p>0:10 Каталоги паттернов</p><p>2:50 AbstractFactory</p><p>15:18 Strategy</p><p>21:46 Proxy</p><p>Во время прохождения уроков этого видеокурса вы сможете разработать полноценное первое приложение на Python.</p><p>Первый урок — <a href="https://tproger.ru/video/video-osnovy-python-i-razrabotka-pervogo-prilozhenija-s-pomoshhju-fastapi-urok-1/?autoload=1">основы Python</a>.</p><p>Второй урок — <a href="https://tproger.ru/video/video-neobychnyj-python-cikly-klassy-i-dekoratory-urok-2/?autoload=1">циклы, наследование и абстракции</a>.</p><p>Третий урок — <a href="https://tproger.ru/video/video-neobychnyj-python-polimorfizm-inkapsuljacija-i-peregruzka-metodov-urok-3/?autoload=1">полиморфизм и инкапсуляция</a>.</p><p>Четвёртый урок —<a href="https://tproger.ru/video/video-neobychnyj-python-interfejsy-i-patterny-urok-4/?autoload=1">интерфейсы и паттерны</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Топ-5 архитектурных паттернов для распределённых систем</title>
      <link>https://tproger.ru/translations/top-5-arhitekturnyh-patternov-dlja-raspredeljonnyh-sistem</link>
      <comments>https://tproger.ru/translations/top-5-arhitekturnyh-patternov-dlja-raspredeljonnyh-sistem?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Олег Борисенков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/top-5-arhitekturnyh-patternov-dlja-raspredeljonnyh-sistem</guid>
      <description><![CDATA[<p>Пять архитектур распределённых систем с плюсами, минусами и областями применения: такие паттерны помогают крупным веб-приложениям оставаться отзывчивыми.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/top-5-arhitekturnyh-patternov-dlja-raspredeljonnyh-sistem">Топ-5 архитектурных паттернов для распределённых систем</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 08 Jun 2021 13:26:10 GMT</pubDate>
      <content:encoded><![CDATA[<p>Распределённые приложения — главный элемент современной индустрии разработки ПО. Они имеют решающее значение для облачных сервисов хранения данных и позволяют веб-приложениям с огромной аудиторией оставаться реактивными. Для того чтобы эффективно проектировать эти системы, программисты используют фундаментальные блоки — паттерны распределённых систем.</p><p>Мы рассмотрим пять архитектур распределённых систем, их плюсы, минусы и области применения.</p><h2>Что такое паттерн распределённой системы?</h2><p>Паттерны проектирования — это проверенные способы создания систем, каждый из которых подходит для определённого случая. Паттерны абстрактны и не опираются на конкретную реализацию. Большинство паттернов разрабатываются и обновляются множеством разработчиков на протяжении многих лет. Поэтому они зачастую очень эффективны на старте разработки.</p><p>Паттерны — это строительные блоки, которые позволяют программистам использовать существующие знания вместо того, чтобы начинать с нуля строительство каждой системы. Кроме того они имеют набор стандартных моделей, которые помогают другим разработчикам понять, как их проекты могут взаимодействовать с данной системой.</p><p>Паттерны распределённых систем описывают взаимодействие различных узлов системы и то, как они обрабатывают задачи и виды процессов для различных задач.</p><p>Эти паттерны широко используются в архитектуре крупномасштабных облачных вычислений и систем масштабируемых микросервисов.</p><h3>Типы паттернов распределённых систем</h3><p>Большинство паттернов распределённых систем попадает в одну из трёх категорий, в зависимости от функциональности с которой они работают:</p><ul><li>Взаимодействие объектов: описывают протоколы отправки сообщений и разрешения для общения между компонентами системы.</li><li>Безопасность: обеспечивают конфиденциальность, целостность и доступность для защиты системы от несанкционированного доступа.</li><li>Событийные: описывают создание, определение, использование и реакцию на события системы.</li></ul><h2>1. Разделение ответственности на команды и запросы (CQRS)</h2><p>Этот паттерн предполагает разделение операций чтения и записи для увеличения масштабируемости и безопасности системы. Он использует команды для записи данных в постоянное хранилище (они ничего не возвращают) и запросы для обнаружения и получения данных (не могут изменять данные).</p><p>Команды и запросы обрабатываются центром управления, который получает запросы от пользователей. Затем центр получает данные и выполняет необходимые изменения, сохраняет их и уведомляет сервис выполняющий чтение. Этот сервис обновляет модель чтения и показывает изменения пользователю.</p><p>Плюсы:</p><ul><li>Уменьшение сложности системы благодаря делегированию.</li><li>Обеспечение чёткого разделения между бизнес-логикой и валидацией.</li><li>Разделение ответственности.</li><li>Уменьшение количества непредсказуемых изменений распределённых данных.</li><li>Уменьшение числа сущностей, которые могут изменять данные.</li></ul><p>Минусы:</p><ul><li>Требует постоянного взаимодействия между командами и запросами.</li><li>Может увеличить задержку при отправке большого количества запросов.</li><li>Нет средств связи между сервисными процессами.</li></ul><h3>Область применения</h3><p>CQRS отлично подходит для приложений, интенсивно использующих данные, например систем управления базами данных SQL или NoSQL. Также паттерн используется для архитектур микросервисов с большим объемом данных. Он прекрасно подходит для приложений, сохраняющих состояние благодаря разделению на писателя и читателя.</p><h2>2. Двухфазная фиксация (2PC)</h2><p>2PC похож на CQRS использованием транзакций и центра управления, но здесь разделение производится на основании того, на какой стадии находится транзакция. Есть две фазы: фаза подготовки (в которой центр управления сообщает службам подготовить данные) и фаза фиксации (которая сигнализирует службе отправить подготовленные данные).</p><p>Все сервисы в 2PC по умолчанию заблокированы и не могут отправлять данные. После завершения подготовки координатор по одному разблокирует сервисы и запрашивает их данные. Если сервис не готов подтвердить данные, координатор переходит к другому сервису. Когда все подготовленные данные отправлены сервисы остаются разблокированными и ожидают задачи от координатора.</p><p>2PC гарантирует, что одновременно может работать только одна служба, что делает процесс более устойчивым и последовательным, чем CQRS.</p><p>Плюсы:</p><ul><li>Консистентность и устойчивость к ошибкам из-за отсутствия параллельных запросов.</li><li>Масштабируемость — может обрабатывать большие пулы данных так же эффективно, как и данные с одного компьютера.</li><li>Поддерживает одновременную изоляцию и разделение данных.</li></ul><p>Минусы:</p><ul><li>Не отказоустойчив, подвержен возникновению узких мест и блокировок из-за своей синхронной природы.</li><li>Требует больше ресурсов, чем другие паттерны.</li></ul><h3>Область применения</h3><p>2PC лучше всего подходит для распределенных систем, имеющих дело с транзакциями, которые отдают предпочтение точности, а не эффективности использования ресурсов. Он устойчив к ошибкам и позволяет легко отследить ошибки даже в больших объёмах информации.</p><h2>3. Saga</h2><p>Saga — это асинхронный паттерн не использующий центр управления. Сервисы здесь сами взаимодействуют между собой. Эта особенность позволяет избавиться от недостатков упомянутых выше паттернов.</p><p>Для связи между сервисами в Saga используется шина событий. Шина передаёт запросы между службами, и каждая участвующая служба создает локальную транзакцию. Затем участвующие службы выдают событие для получения другими службами. Все другие службы прослушивают события. Первая служба, получившая событие, выполнит необходимое действие. Если этой службе не удается выполнить действие, оно отправляется в другие службы.</p><p>Эта структура похожа на 2PC тем, что службы циклически запускаются, если кто-то не может выполнить задачу. Тем не менее, Saga не использует центр управления, чтобы лучше управлять потоком и уменьшить количество требуемой обратной связи.</p><p>Плюсы:</p><ul><li>Отдельные сервисы могут обрабатывать более долгие транзакции.</li><li>Децентрализация отлично подходит для распределённых систем.</li><li>Меньше узких мест благодаря одноранговой связи между службами.</li></ul><p>Минусы:</p><ul><li>Асинхронная независимость делает трудным отслеживание сервисов выполняющих индивидуальные задачи.</li><li>Сложный механизм управления затрудняет отладку.</li><li>Меньшая изоляция сервисов, чем в предыдущих паттернах.</li></ul><h3>Область применения</h3><p>Этот паттерн распределённой системы хорошо подходит для задач, которым требуется масштабируемая беcсерверная архитектура, способная обрабатывать много запросов одновременно. AWS использует подобные решения в <a href="https://aws.amazon.com/ru/step-functions/?step-functions.sort-by=item.additionalFields.postDateTime&amp;step-functions.sort-order=desc">AWS Step Functions</a> и <a href="https://aws.amazon.com/ru/lambda/">AWS Lambda</a>, и других продуктах.</p><h2>4. Реплицированные сервисы с распределением нагрузки (RLBS)</h2><p>RLBS — это самый простой и часто используемый шаблон проектирования. На базовом уровне он состоит из нескольких идентичных сервисов, которые общаются с центральным распределителем. Каждый сервис способен выполнять задачи и перезапускать их в случае неудачи. Распределитель получает запросы от конечного пользователя и разделяет их между сервисами, используя round-robin или более сложный алгоритм.</p><p>Дублирующие службы обеспечивают высокую доступность приложения для запросов пользователей и могут перераспределять работу в случае сбоя одного экземпляра службы.</p><p>RLBS часто используется с Azure Kubernetes, которая представляет собой технологию оркестровки контейнеров с открытым исходным кодом, разработанную Microsoft, которая предлагает автоматическое масштабирование служб в зависимости от воркфлоу.</p><p>Плюсы:</p><ul><li>Стабильная производительность с точки зрения конечного пользователя.</li><li>Быстрое восстановление после сбоев.</li><li>Высокая масштабируемость через увеличение числа сервисов.</li><li>Отличная многопоточность.</li></ul><p>Минусы:</p><ul><li>Нестабильная производительность, зависящая от алгоритма балансировки.</li><li>Управление сервисами требует много ресурсов.</li></ul><h3>Область применения</h3><p>RLBS отлично подходит для систем, с которыми пользователи взаимодействуют напрямую. Нагрузка на эти системы в течение дня изменяется, поэтому требуется низкая задержка, например Netflix или Amazon Prime.</p><h2>5. Шардинг</h2><p>Альтернативой репликации является создание отдельных сервисов, каждый из которых выполняет определённый тип запросов. Это называется шардингом, потому что вы разделяете запрос на несколько неодинаковых частей. Например, у вас может быть отдельный сервис, который принимает кэширующиеся запросы, а другой сервис будет отвечать за запросы с высоким приоритетом. Распределитель нагрузки обрабатывает каждый запрос и передаёт его в подходящий сервис.</p><p>Шардинг сервисов обычно используется для создания сервисов с поддержкой состояния, потому что объём состояния часто слишком большой для одного stateless контейнера. Шардинг позволяет масштабировать отдельные элементы под размер состояния.</p><p>Шардинг также позволяет быстрее обрабатывать высокоприоритетные запросы. Сегменты, предназначенные для запросов с высоким приоритетом, всегда доступны для обработки таких запросов в момент их поступления и не требуют размещения запроса в очереди.</p><p>Плюсы:</p><ul><li>Возможность масштабировать сегменты для общих запросов.</li><li>Легкая приоритизация запросов.</li><li>Простая отладка, благодаря естественной сортировке.</li></ul><p>Минусы:</p><ul><li>Поддержка большого числа сегментов может потребовать много ресурсов.</li><li>Непропорциональное использование сегментов может привести к потерям производительности.</li></ul><h3>Область применения</h3><p>Шардинг сервисов хорош в тех случаях, когда ваша система работает с предсказуемой несбалансированной нагрузкой для разных запросов, а некоторые запросы имеют приоритет.</p><h2>Что дальше?</h2><p>В статье были рассмотрены лишь несколько паттернов распределённых систем. Вот ещё несколько шаблонов проектирования для изучения:</p><ul><li>Sidecar паттерн;</li><li>Упреждающая журнализация (Write-Ahead Log);</li><li>Split-Brain паттерн;</li><li>Направленная отправка (Hinted Handoff);</li><li>Чтение с восстановлением (Read Repair).</li></ul><p>Мы спросили экспертов о том, <a href="https://tproger.ru/experts/which-design-patterns-to-learn/">какие шаблоны проектирования стоит знать каждому</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Паттерн ООП «Хранитель»</title>
      <link>https://tproger.ru/articles/pattern-oop-hranitel</link>
      <comments>https://tproger.ru/articles/pattern-oop-hranitel?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Мария Кривоченко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/pattern-oop-hranitel</guid>
      <description><![CDATA[<p>Обсудим паттерн ООП проектирования Хранитель на примере текстового редактора, который меняет форматирование текста и других элементов</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/pattern-oop-hranitel">Паттерн ООП «Хранитель»</a>»</p>]]></description>
      <category><![CDATA[Объектно-ориентированное программирование]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Гостевая публикация]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 11 May 2021 11:20:26 GMT</pubDate>
      <content:encoded><![CDATA[<p>«Хранитель» (Memento), также известный как Снимок – поведенческий паттерн проектирования. Он позволяет определять, сохранять, а также восстанавливать предыдущие состояния объектов без нарушения принципа инкапсуляции.</p><p>Самый простой и наглядный пример использования этого паттерна – некий текстовый редактор, который позволяет изменять форматирование текста и других элементов. Но при этом пользователь может эти изменения отменить. Другой пример – восстановление состояния персонажей в игре на контрольных точках.</p><p>Формально, в виде диаграмм структуру паттерна можно представить так:</p><p>Участники процесса:</p><ul><li>Memento («хранитель») – хранитель, сохраняет состояние объекта Originator;</li><li>Originator («создатель») – создает экземпляр объекта хранителя. Имеет полный доступ к Memento;</li><li>Caretaker («опекун») – производит сохранения состояний.</li></ul><p>Теперь рассмотрим очень упрощённый пример текстового редактора. У него будет всего пять команд:</p><ul><li>добавление нового блока текста;</li><li>установка стиля текста;</li><li>вывод всего текста на экран;</li><li>сохранение текущего состояния документа;</li><li>отмена последнего действия по редактированию документа.</li></ul><p>Для начала создадим класс нашего документа – класс Doc. Он будет содержать в себе два параметра – текст и стиль, которые пользователь может изменить с помощью соответствующих методов – AddBlock(string text) и SetStyle(int style). Выводить содержимое будем через метод Print().</p><p>Далее нам нужно создать класс для хранения состояния документа – DocMemento.</p><p>В данном случае это своего рода контейнер-копия сохранённого состояния объекта Doc. Мы передаём не копию экземпляра документа, а только его состояние со значимыми параметрами.</p><p>Теперь снова вернёмся к классу Doc и добавим два метода: для сохранения в объект-memento и восстановления состояния из объекта-memento:</p><p>Создадим класс, который будет в себе содержать историю изменений документа – EditorHistory. Вся история состояний будет храниться в стеке, который будет скрыт от пользователя для доступа напрямую.</p><p>Всё готово, теперь можно создать класс редактора и наполнить пользовательскими действиями:</p><p>Для примера мы сделали сохранение состояния после ввода блока текста «Привет, мир!» и смены параметра стиля текста. Сохранили в объекте history, снова изменили документ и вернули прежнее состояние. Каждое изменение сопроводили выводом всего документа на экран, то есть в консоль.</p><p>Если сравнивать с представленной в самом начале схемой, то в роли Originator у нас выступает Doc, Memento – DocMemento, а в роли Caretaker – EditorHistory. Документу доступны все поля, поэтому именно он делает снимок. А из истории берёт состояние для восстановления.</p><p>Данный пример слишком простой. Добавляя новые структуры и объекты в наш редактор, мы будем усложнять состояние документа (добавятся значения для разных текстовых блоков, страниц и абзацев, геометрические объекты, рисунки и тому подобное). Поэтому для более удобного представления состояния документа нам придётся использовать отдельные классы контейнеров данных, которые будут содержать множество полей. Таким образом, в более сложных программах для хранения состояний может потребоваться много памяти, если снимков будет много – это, пожалуй, основной недостаток паттерна.</p><p>Есть и чуть более сложные вариации реализации данного паттерна – создавая пустой промежуточный интерфейс или же более широкий вариант, с возможностью иметь множество видов создателей и снимков. Последний, например, позволяет полностью исключить доступ к состоянию создателей и снимков, но при этом сам опекун становится независимым от создателей.</p><p>Очень часто паттерн «Хранитель» совместно используется с паттерном «Команда» (как раз для выполнения команд «Сохранить» и «Восстановить»).</p><p>Итого, «Хранитель» позволяет нам передавать сохраняемые состояния объекту, но не передавать ему управление самим сохраняемым объектом, сохраняя инкапсуляцию.</p>]]></content:encoded>
    </item>
    <item>
      <title>ООП паттерн Visitor — объяснение и пример использования</title>
      <link>https://tproger.ru/articles/oop-pattern-visitor-objasnenie-i-primer-ispolzovanija</link>
      <comments>https://tproger.ru/articles/oop-pattern-visitor-objasnenie-i-primer-ispolzovanija?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/oop-pattern-visitor-objasnenie-i-primer-ispolzovanija</guid>
      <description><![CDATA[<p>Классический пример наследования прямоугольника и квадрата показывает, какие ожидания пользователя нарушаются при изменении ширины и высоты фигур.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/oop-pattern-visitor-objasnenie-i-primer-ispolzovanija">ООП паттерн Visitor — объяснение и пример использования</a>»</p>]]></description>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Scala]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 31 Mar 2021 09:30:35 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Полиморфизм и LSP</h2><p>Рассмотрим классический пример наследования, когда требуется реализовать два класса типа «Прямоугольник» и «Квадрат». Известно, что у каждой фигуры есть периметр и площадь. Будем считать, что объект мутабельный (mutable ― изменчивый) и имеет соответствующие методы (свойства) для изменения размеров. Достаточно легко обобщить формулу для вычисления площади и периметра таких фигур, и, кажется, логично будет использовать наследование. Кто в такой модели должен быть родительским классом, а кто ― потомком?</p><p>Казалось бы, со школы мы помним, что квадрат ― частный случай прямоугольника, а значит, квадрат наследует от прямоугольника.</p><p>Пока все просто. Но что же произойдёт, если мы попытаемся создать «частный случай прямоугольника»? Ведь у квадрата по определению width == height. Конечно же, можно использовать приватную переменную, и при модификации ширины менять и высоту фигуры соответственно, но, если задуматься ― а насколько это ожидаемо? Если квадрат ― наследник прямоугольника, то пользователь должен иметь возможность использовать квадрат точно так же, как и прямоугольник. Может ли пользователь предположить, что при установке ширины прямоугольника автоматически поменяется и его длина?</p><p>Хорошо, если не получается напрямую, давайте попробуем в обратную сторону. Пусть прямоугольник — это такой специальный квадрат, у которого появилась длина.</p><p>Пока выглядит неплохо. Однако, пользователь знает, что площадь квадрата ― квадрат его стороны. Может ли пользователь ожидать, что, установив сторону квадрата в 5, например, он не получит 25 только потому, что его квадрат на самом деле прямоугольник?</p><p>Хорошим дизайном вашей модели будет являться такая, в которой не будет исключений и подводных камней. По идее, только посмотрев на сигнатуру интерфейса, вы должны иметь возможность сказать, как экземпляр этого класса себя будет вести и что от него стоит ожидать. Эта идея носит гордое название «принципа замещения Барбары Лисков» или LSP. Можно грубо сказать, что LSP — это такой ad hoc полиморфизм, при котором класс-потомок никогда не врет.</p><h2>Когда полиморфизм может сделать больно</h2><p>Как известно, полиморфизм — один из «китов», на которых стоит концепция ООП. Сама идея полиморфного состояния настолько мощная и настолько широко используется, что часто можно встретить наследование даже там, где оно и не нужно. Иногда это приводит к нарушению LSP, что может значительно усложнить поддержку существующей кодовой базы. Например, достаточно часто можно встретить реализацию такого вида:</p><p>И ладно, если это часть вашей собственной кодовой базы. Но что, если это некоторый общий модуль, разделяемый между несколькими командами? Или если такой код распространяется в уже скомпилированном виде? Насколько легко будет проверять каждый экземпляр модели на предмет нарушения LSP?</p><p>Подчеркну, что страшно не само по себе нарушение принципа, в конце концов программирование — это раздел инженерии, и вся наша работа состоит в выборе подходящего компромисса — а то, что такое поведение крайне трудно предсказать: откуда разработчику знать, что метод, который он вызовет, выбросит Exception? Обратите внимание, что такое исключение неожиданно и не может быть предсказано исходя из здравого смысла — ведь рядом может лежать другая имплементация, которая не имеет подобной проблемы. Некоторые ЯП имеют checked exception, но практика показывает, что их проще завернуть в unchecked, чем пытаться поддерживать. Как следствие, логическая сложность программы с подобным code smell растет неоправданно, и становится все сложнее понять, какая из N имплементаций сервиса будет работать как ожидается, а какая ― нет.</p><p>При проектировании модели можно услышать советы вроде «придерживайся tell-don’t-ask», «используй null-object-pattern», или даже «prefer composition over inheritance», которые в целом могут помочь увернуться от описанной проблемы. Последний совет, кстати, вообще несколько неочевиден — не использовать наследование в ООП специфичном языке.</p><p>Я предлагаю посмотреть в корень — и он в том, что данные хранятся вместе с поведением. В нашей жизни мы часто думаем об окружающих вещах как об объектах, поэтому такое положение дел кажется более или менее естественным. С другой стороны, при достаточно большой и сложной доменной области наследование может начать нести некоторую опасность ― ведь описать формальным языком поведение, не нарушая LSP, бывает очень сложно. На мой взгляд, эта проблема возникает в первую очередь из-за того, что в реальной жизни мы, скорее, создаем свою иерархию наследований под каждый конкретный случай, тогда как при проектировании доменной области зачастую стараемся сделать из объекта некий «швейцарский нож», обладающий разным и порой никак не связанным друг с другом поведением — потому что чем меньше вспомогательных сущностей мы имеем, тем проще понять модель.</p><h2>«Не узнаю вас в гриме»</h2><p>Вторая проблема более характерна для строго типизированных языков, где, имея список каких-то объектов, приведенных в базовому типу, нужно проверять тип каждого элемента, чтобы вызвать специфическое поведение. Например:</p><p>Такое часто можно встретить при вызове сторонних библиотек, или если у вас legacy. Некоторые языки программирования даже имеют более или менее стандартную функциональность, чтобы работать с базовыми типами, проверяя их конкретную реализацию в рантайме, что ломает сразу двух из трех китов ООП ― и полиморфизм, и инкапсуляцию. Например, если вы счастливый программист на Scala ― у вас есть pattern matching. С другой стороны, никто не мешает сделать своего китоломателя на минималках, и это и есть — Visitor.</p><h2>Идиоматичный Visitor</h2><p>Идея достаточно простая — вместо того, чтобы объявлять поведение внутри класса, мы делегируем это поведение некоторому внешнему объекту. При этом объект-делегат называется посетителем (Visitor), и в нем должны быть объявлены методы посещения для каждого конкретного типа из иерархии. Экземпляр такого объекта мне нравится называть глаголом, описывающим нужный эффект, тем самым подчеркивая, что визитор — скорее поведение, отделенное от данных, чем полноценный объект (makeSomething vs somethingMaker).</p><p>Пора посмотреть, как могла бы выглядеть реализация паттерна:</p><p>Приведенный пример легко можно переделать, чтобы собирать только фастфуд (Sausage) или сериализовать в json, или добавлять любое другое поведение в существующую иерархию объектов. В этом самое большое преимущество паттерна.</p><p>А вот недостатком будет многословность и не слишком большая очевидность. Для каждого потомка Tasty придется делать реализацию visit, а в самом TastyVisitor ― сделать соответствующий метод.</p><p>К тому же этот шаблон не получится использовать, если нет контроля над существующим деревом объектов ― то есть если нельзя внести соответствующие изменения в существующую иерархию.</p><h2>Рекомендация</h2><p>Если у вас уже есть не слишком большая цепочка доменных классов, которые могут использовать в разных сценариях — задумайтесь о том, а не нарушите ли вы принцип единичной ответственности уже сейчас, и не выйдет ли так, что полиморфное поведение будет больше мешать, чем помогать? И если все-таки не хочется иметь разные доменные модели под каждый конкретный сценарий использования, например, потому что они будут на 99% совпадать, то может быть имеет смысл отделить данные от поведения — и Visitor в таком случае может сослужить хорошую службу. Ценой одного дополнительного интерфейса на этапе проектирования и изменением привычного способа взаимодействия с классом модели вы сможете добавлять любое необходимое поведение в типобезопасной манере, и каждый такой метод будет изолирован от других. Получается, что соблюдается и принцип единичной ответственности, и LSP не нарушается.</p>]]></content:encoded>
    </item>
    <item>
      <title>Стоит прочитать: обзор книги Алана Купера «Психбольница в руках пациентов»</title>
      <link>https://tproger.ru/books/obzor-knigi-alana-kupera-psihbolnica-v-rukah-pacientov</link>
      <comments>https://tproger.ru/books/obzor-knigi-alana-kupera-psihbolnica-v-rukah-pacientov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/books/obzor-knigi-alana-kupera-psihbolnica-v-rukah-pacientov</guid>
      <description><![CDATA[<p>Книга приоткрывает культуру разработки и объясняет происхождение перегруженных интерфейсов и неочевидных функций, которыми напичканы программы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/books/obzor-knigi-alana-kupera-psihbolnica-v-rukah-pacientov">Стоит прочитать: обзор книги Алана Купера «Психбольница в руках пациентов»</a>»</p>]]></description>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Стоит прочитать]]></category>
      <category><![CDATA[Книги]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 24 Dec 2020 11:28:11 GMT</pubDate>
      <content:encoded><![CDATA[<p>Наверняка каждый сталкивался с такими устройствами и программами, которые буквально «напичканы» сотней полезных дополнительных функций или кнопок. Кажется, что ты приобрел такой универсальный нож, который может все. На деле оказывается, что пользоваться такой вещицей совершенно неудобно, и проще заменить ее на что-то более простое. В таких случаях разработчик едва ли думает о трудностях пользователя, но только не Алан Купер, который решил разобраться в проблеме.</p><p>На написание статьи меня сподвиг мой опыт и мои боли, через которые пришлось пройти. Придя в тестирование из совершенно иной инженерной области, я хотел сформировать представление о полном цикле производства программного продукта.</p><p>Я перелопатил тонны литературы, но одной из любимых книг стала книга Алана Купера «Психбольница в руках пациентов», написанная в начале нулевых. События книги затрагивают в том числе и девяностые, но это не мешает ей быть актуальной даже в 2020. Поэтому я решил выписать некоторые мысли Алана Купера.</p><p>Читая книгу, я невольно вспоминал те проблемы, которые возникали каждый раз, когда мне приходилось изучать новый программный продукт, неважно, будь то десктопное приложение, новый телефон или очередное обновление операционной системы. Я получал негативный опыт, но не понимал, почему есть настолько неочевидные функции, непонятно скомпонованное меню и перегруженный интерфейс. Книга приоткрывает занавес культуры разработки и объясняет, почему так происходит.</p><p>В начале книги автор рассказывает о личном опыте использования программных продуктов. Он, человек более 20 лет находящийся в индустрии, не может справиться со своим будильником, а управление функциями телевизора приводит его в тупик. Эти и другие сложности подводят его к мысли о том, что в погоне за новыми функциями, за менеджерскими достижениями производители забывают о конечном пользователе.</p><p>«Мы даем вам много функций, вы должны быть счастливы», — говорят производители. Но какой от этих функций толк, если пользователь не понимает, зачем они нужны, или не может ими пользоваться ввиду чрезвычайной сложности. Зачастую такое происходит из-за сжатых сроков, установленных топ-менеджментом. В итоге UI/UX делаются  в последнюю очередь, после реализации бизнес-логики.<br /></p><p>Пользователям важно лишь то, насколько удобно решать нужные им задачи с помощью конкретного продукта. В редких случаях опции тоже могут пригодиться при решении задач, но чаще случается так, что опции только затрудняют работу и запутывают пользователя. Более того, опции, польза которых неочевидна, заставляют пользователей чувствовать себя глупыми.</p><p>Забывая о том, что программа должна помогать пользователю достигать его целей,  разработчики и менеджеры делают программы, отражающие механизм их устройства. Разумеется, сами программисты не испытывают неудобств при обращении с такой программой, так как понимают, как она устроена, и, следовательно, знают, как с ее помощью решать задачи.</p><p>Люди же вынуждены пользоваться этими плохими программами по нескольким причинам:</p><ol><li>Это их рабочий инструмент, и они боятся потерять работу. Обычно это корпоративные пользователи.</li><li>Отсутствие альтернатив. Актуально для специфического софта, позволяющего решать определенные задачи.</li></ol><p>Чтобы пользователи могли быть довольны программами и эффективно выполняли с их помощью свои задачи, программы должны быть созданы с учетом требований человеческой природы.</p><p>Автор уверен, что нельзя доверять проектирование систем и интерфейсов программистам и менеджерам. Этим должны заниматься специалисты по проектированию UX/UI.</p><p>Чтобы решить проблему эффективно, потребуется приобщиться к образу  мышления программистов и понять, как замотивировать их создавать интерактивные продукты, приятные пользователям. Проектировщику взаимодействия важно обладать знаниями психологии, но сюда должно входить не только понимание психологии пользователя, но и психологии разработчиков программных продуктов.</p><h2>Как решить проблему и с чего начать проектирование?</h2><p>Разработайте детальное описание потенциального пользователя вашего продукта и его намерений. Для этого автор предлагает использовать метод персон. Персоны — это портреты выдуманных пользователей, под нужды которых создается проект.</p><p>Советы по созданию персон от Алана Купера:</p><ol><li>Ваш продукт станет куда более успешным, если вы спроектируете его только для одного человека.</li><li>Чем конкретнее мы прописываем характеристики персон, тем более эффективны они в процессе проектирования.</li><li>Важно не смешивать точное определение персоны пользователя с реальными людьми.</li></ol><p>Автор указывает на следующие плюсы такой персонализации:</p><ol><li>Одним из ценных моментов использования персон является то, что они задают более реалистичный тон всем дискуссиям об уровне компьютерной грамотности. Степень подготовленности пользователей может варьироваться в очень широких пределах, и персоны позволяют отчетливо это увидеть.</li><li>Второй чрезвычайно важной и ценной особенностью персон является их способность выступать в качестве великолепного инструмента коммуникации. Набор образов превращается в систему проектирования, обладающую выразительной силой, позволяющей донести наши представления относительно проектирования. Куда больше они, подобно прожектору, показывают программистам, маркетологам и руководителям, что мы принимаем совершенно верные решения по проектированию.</li></ol><h2>Как создавать образы персон?</h2><p>Для каждого проекта составляется отдельный набор образов, включающий от трех до двенадцати уникальных персон. При этом проектирование осуществляется не для всех из них, но важен каждый образ для отражения состава пользователей. Некоторые персоны описываются лишь для того, чтобы понять, для кого мы точно проектировать не будем.</p><p>Любой набор образов потенциальных пользователей содержит хотя бы одну ключевую персону. Этот главный образ становится центром процесса проектирования. Ключевой является такая персона, которая обязательно должна быть довольна интерфейсом, но при этом она не может быть довольна, если интерфейс спроектирован под какую-то другую персону.</p><h2>Как применять персоны?</h2><p>В первую очередь необходимо определить цели, которые каждая из персон преследует. Два аспекта: персоны и цели – неотделимы друг от друга. Суть персоны – в достижении каких-либо целей, а цели, в свою очередь, наполняют смыслом персону. Проектирование под цели персоны пользователя позволяет нам явно увидеть альтернативные способы предоставления функциональных возможностей. Как правило, с помощью такого подхода получается найти лучшие способы решения типичных проблем проектирования.</p><p>Целью проектировщика взаимодействия является создание желанной для пользователя программы. Мы должны проектировать ее поведение так, чтобы программа вела себя как симпатичный нам человек. Персоны и цели помогают создать программный продукт с поведением, похожим на поведение хорошего сотрудника и отвечающим следующим требованиям обходительной программы:</p><ul><li>программа интересуется мной;</li><li>программа относится ко мне с почтением;</li><li>программа ведет себя приветливо;</li><li>программа обладает здравым смыслом;</li><li>программа предугадывает мои потребности;</li><li>программа обладает отзывчивостью;</li><li>программа не спешит жаловаться на личные проблемы (обходительная программа всегда в курсе происходящего);</li><li>программа обладает проницательностью;</li><li>программа характеризуется уверенностью в своих силах (обходительная программа концентрируется на важном);</li><li>программа позволяет обойти правила;</li><li>программа поощряет незамедлительно;</li><li>программа вызывает доверие.</li></ul><p>Подводя итог по книге, хотелось бы отметить ее практическую ценность и понятную большинству подачу материала. Технологии и принципы разработки ПО ушли вперед, но они ничуть не перечеркнули идеи Алана Купера: появились гибкие методологии Agile, которые прекрасно вписывают в свой концепт этапы прототипирования и проектирования UI/UX.  По моему убеждению, для старта в IT эта книга обязательна к прочтению.</p>]]></content:encoded>
    </item>
    <item>
      <title>Какие шаблоны проектирования стоит знать каждому программисту — отвечают эксперты</title>
      <link>https://tproger.ru/experts/which-design-patterns-to-learn</link>
      <comments>https://tproger.ru/experts/which-design-patterns-to-learn?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Никита Прияцелюк]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/experts/which-design-patterns-to-learn</guid>
      <description><![CDATA[<p>Эксперты советуют программистам изучить шаблоны Адаптер, Декоратор, Заместитель, Цепочка обязанностей, Наблюдатель, Стратегия и другие.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/experts/which-design-patterns-to-learn">Какие шаблоны проектирования стоит знать каждому программисту — отвечают эксперты</a>»</p>]]></description>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Ответы экспертов]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 28 Aug 2019 14:48:42 GMT</pubDate>
      <content:encoded><![CDATA[<p>Шаблонов проектирования существует достаточно много, и часто новички теряются, нужно ли знать их все или для начала достаточно изучить несколько ключевых. На что обратить внимание в первую очередь? И стоит ли вообще тратить на них время? На эти вопросы ответят наши эксперты.</p><p>Освоение шаблонов проектирования — это один из обязательных этапов в обучении начинающих разработчиков. Многие задачи, с которыми они сталкиваются, уже имеют типовые решения, которые помогают не только сэкономить время, но и избежать неочевидных ошибок. Кроме этого, знание шаблонов позволяет проще доносить свои идеи до команды и проще понимать существующие решения.</p><p>На мой взгляд, новичкам для начала надо разобраться с шаблонами Адаптер (Adapter), Декоратор (Decorator) и Заместитель (Proxy), т. к. они относительно простые для понимания, много где используются и позволяют понять подход. Эти шаблоны во многом схожи, но каждый решает разную задачу. Важно понимать их отличия.</p><p>После этого я бы рекомендовал изучить шаблоны Цепочка обязанностей (Chain of Responsibility), Наблюдатель (Observer), Стратегия (Strategy) и Спецификация (Specification), т. к. они описывают важные концепции, которые сейчас используются во многих фреймворках.</p><p>На самом деле паттернов проектирования не так уж и много, но в действительности некоторые из них используются реже других. Если очень грубо, то паттерны можно разделить на три части. Порождающие паттерны — они используются для проектирования новых объектов и коллекций объектов. Структурные — нужны для упорядочивания и унификации свойств различных объектов и классов. И поведенческие — описывают взаимодействие между объектами и сервисами. Я бы рекомендовал начать с порождающих паттернов и изучить их достаточно хорошо. Благо их не много, и они являются основой для остальных паттернов и архитектуры в целом. Старайтесь не только изучать паттерны, но и сразу применять их на практике. Иногда бывает полезно пересмотреть уже завершённые проекты с учётом новых знаний и перепроектировать их.</p><p>Шаблоны проектирования отличаются в разных экосистемах. Я рекомендую начать не с поиска универсальных, а с изучения принятых в вашей экосистеме. Например, фронтенд-разработчику, который использует React+Redux нужны одни шаблоны проектирования, а разработчику бэкенда на Go — совсем другие. Рекомендую начать с детального изучения своей экосистемы и принятых там подходов. Когда со своей экосистемой будет относительно понятно, то можно продолжить изучение подходов любой смежной области для расширения кругозора.</p><p>Кстати, классическая книга Design patterns 1994 ориентирована на задачи, актуальные для тогдашней разработки на C++. В 2019 году лучше поискать более свежие источники информации. Можно начать с поиска «technology_name best practices» — на первой странице будут не только рекламные тексты, но и 2–3 полезных результата.</p><p>Для выбора решения недостаточно знать подходящие шаблоны проектирования. Не менее важно исключить неподходящие. Для этого я рекомендую изучать «анти-шаблоны» — подходы, про которые известно, что они приводят к плохим результатам.</p><p>У каждого подхода есть область применения, за пределами которой этот подход перестает хорошо работать. Не всегда можно найти прямое описание границ применимости. Советую обращать внимание на ситуации, когда один и тот же подход в одном месте называют шаблоном, а в другом анти-шаблоном. Когда вы поймете, чем отличаются эти ситуации и почему тот или иной подход перестает работать, то станете лучше чувствовать границы применимости.</p><p>В моей практике шаблоны, или паттерны проектирования, были и остаются скорее хорошей историей для собеседований — я встречал не так много людей, которые действительно ими мыслят и используют для решения задач. Тем не менее, знать основные и понимать разницу между их видами полезно.</p><p>Чаще всего в своей работе я встречаюсь с порождающими и структурными паттернами, такими как синглтон, фабрика, фабричный метод, фасад, адаптер. Первые два отвечают за контролируемое создание объектов, вторые — за связывание между собой различных компонентов или подсистем.</p><p>Что касается поведенческих паттернов, они, на мой взгляд, реже применяются. Дело в том, что правильная структура многих из них непроста, и, если прямо следовать предписанному проектированию, они могут чрезмерно усложнять структуру программы. Поэтому при использовании как этих паттернов, так и других, важно уметь видеть картину, происходящего целиком, и трезво оценивать целесообразность.</p><p>Под словосочетанием «шаблоны проектирования» я пониманию некие «лучшие практики», набор удачных подходов в решении какого-то узкого круга задач.</p><p>Разработчики же рассказывают, что на собеседованиях их зачастую настойчиво спрашивают про шаблоны GOF. На мой взгляд, знание конкретных шаблонов, в частности GOF, крайне переоценено в сообществе.</p><p>Есть вещи более важные, которые следовало бы знать каждому разработчику. Они гораздо сильнее влияют на эффективность и качество работы, чем знание конкретных примеров: «Фабрика», «Шаблонный метод» и т. д. Например, понимание, как работает операционная система, или что является узким местом в архитектуре современных компьютеров.</p><p>Конечно, изучать паттерны нужно, но я бы не стал выделять какой-то их конкретный набор. Советую обратить внимание в первую очередь на ваши рабочие задачи и изучать best practices по их решению. Например, если вы работаете с контейнерами, вам стоит познакомиться с Sidecar и Ambassador. Если работаете с UI, для вас будет полезно разобраться в словосочетании «конечный автомат» и познакомиться с MVC. Люди, работающие с инфраструктурой, могут почитать про Immutable server. Java-разработчики часто знакомы с Inversion of Control и Dependency Injection, но многим будет полезно знать и про Disruptor, Tolerant Reader, Event Sourcing.</p><p>Если же отвечать на поставленный вопрос кратко, то изучайте те шаблоны, которые помогают решать ваши ежедневные задачи более эффективным способом. Нет единого набора, который стоит знать каждому разработчику. Всё зависит от решаемых вами задач.</p><p>Большая часть сложных шаблонов, которые стали популярны в 90-х после выхода книги «Паттерны проектирования», сходит на нет. Сейчас все стремятся к простым решениям — пишут на JavaScript, Python и т. п. Там всё проще, в подобных языках без строгой поддержки типов сложные конструкции практически невозможно использовать. В целом, зубрить что-либо не стоит. Намного важнее иметь чувство кода. Если пишешь код и видишь, что что-то идёт не так, а ты сам не понимаешь, как сделать лучше, то можно обратиться к профессиональной литературе. Могу посоветовать книгу Стива Макконнелла «Совершенный код. Мастер-класс», например. Если хороший программист видит сложную ситуацию при проектировании, он её всё равно проработает вне зависимости от знаний конкретных шаблонов. Однако понимание шаблонов нужно для коммуникации. Современные программисты всё время общаются, что-то обсуждают. В этом общении есть свой жаргон, в котором присутствуют в том числе шаблоны. Это в основном довольно простые вещи: wrapper, singleton например. Нужно с ходу понимать, о чём идёт речь, чтобы эффективно коммуницировать с коллегами.</p><p>Я бы посоветовал для начала хорошо изучить сферу и задачи, решения которых вы будете разрабатывать, а затем выяснить, какие именно шаблоны проектирования существуют в этой сфере. Изучить, хотя бы теоретически, нужно все из них, чтобы в нужный момент вам не приходилось «изобретать велосипед» и тратить время на написание нового кода, когда решение вашей задачи уже давно создано кем-то другим.</p><p>Ещё один важный момент, на который стоит обратить внимание — это постоянная актуализация своих знаний о шаблонах. Во многих сферах разработки они довольно часто меняются, и это нужно отслеживать, чтобы не натыкаться на устаревшие функции и элементы.</p><p>Существует, например, весьма известный шаблон проектирования интерфейса — MVC (Model View Control). Его часто применяют в разработке бизнес-приложений, он не привязан к конкретному языку программирования. Шаблон состоит из трёх компонентов: модель данных (Model), пользовательский интерфейс (View), управляющая логика (Control). Реализация каждого из компонентов делается отдельно, а их сочетание позволяет пользователю работать с данными через интерфейс. Такая модель реализации позволяет не только разделять работу по проектированию собственно интерфейса (View), управляющей логики (Control) и функциональной логики приложения (Model), но и создавать различные сочетания этих трёх компонентов.</p><p>Я считаю, что вся совокупность литературы, написанной по теме шаблонов программирования, существует для тех разработчиков, которые уже на хорошем уровне самостоятельно владеют предметом. Ни в коем случае не порекомендовал бы углубление в эти вещи начинающим специалистам.</p><p>Поясню: да, паттерны — объективная реальность разработки, однако «забивать» ими голову в самом начале своего пути в программировании пойдёт только во вред. Вместо того, чтобы доходить до базовых уровней понимания своей работы, новичок может заучить определённый паттерн и далее пытаться встраивать его в проект при любом удобном случае. Не вникать в суть вещей, а выискивать (подчас даже искусственно создавать) ситуацию, когда по его, новичка, мнению, было бы уместно использовать тот или иной паттерн.</p><p>То есть возникает ложный тотем в виде паттерна вместо реального обучения.</p><p>Со временем, когда специалист наберётся опыта, можно (и нужно) изучать паттерны как полезный опыт предшественников, который действительно можно использовать в своей работе по решению поставленной задачи.</p><p>Но до тех пор гораздо полезнее обратиться с вопросом к старшему коллеге и получить от него реальное пояснение по вопросу, что называется, человеческим языком и из первых рук. Это в разы лучше для собственного развития, чем зубрить мёртвые схемы, не вытекающие из собственного опыта профессионального развития.</p><p>Мы делаем сайты на Битрикс, поэтому расскажу про PHP-программистов и frontend-программистов.</p><p>Чем больше шаблонов проектирования знает, а главное понимает, разработчик — тем лучше. Так что знать и понимать стоит всё. Чтобы не теряться, я предложил бы начать с изучения тех шаблонов, которые программист уже применяет, хотя даже не подозревает об этом.</p><p>Например, почти все веб-программисты так или иначе работали с библиотекой jQuery — а это уже целый набор шаблонов:</p><ul><li>Компоновщик (Composite) — работа с множеством объектов как с одним объектом $('.button').hide();</li><li>Адаптер (Adapter) — работа со свойствами и событиями так, будто все браузеры одинаковые $(".container").css({opacity: .5});</li><li>Фасад (Facade) — создание более простых интерфейсов над сложными $.post() = $.ajax({type: "POST"});</li><li>Наблюдатель (Observer) — в принципе вся система событий в JS $(document).on('somethingHappened');</li><li>Итератор (Iterator) — перебор всех объектов $.each(...);</li><li>Заместитель (Proxy) — подмена одного объекта другим с аналогичным поведением. В jQuery — передача контекста this внутрь функции $("button").on("click", function () {$(this).addClass("active");}); внутри реализовано через $.proxy();</li><li>Строитель (Builder) — Создание нескольких объектов одной командой $('&lt;li&gt;&lt;a href="#"&gt;Link&lt;/a&gt;&lt;/ul&gt;').</li></ul><p>В Битрикс программист так же регулярно применяет несколько шаблонов:</p><ul><li>Одиночка (Singleton) — это классы CMain и CDatabase;</li><li>Наблюдатель (Observer) — Система событий в Битрикс — класс EventManager (который ко всему ещё и Одиночка);</li><li>MVC — Модель-представление-контроллер — Компоненты 2.0 в Битрикс;</li><li>Адаптер (Adapter) — абстрагируемся от конкретной базы данных через класс CDatabase;</li><li>ORM — Работа с объектами вместо работы с базой напрямую;</li><li>Пространство имён (Namespace) — уменьшает кол-во глобальных переменных и предотвращает конфликты имён классов, функций и переменных.</li></ul><p>У тех, кто пишет на React, тоже целый набор шаблонов:</p><ul><li>Передача свойств вниз по дереву компонентов;</li><li>условный рендеринг;</li><li>деструктурирование свойств;</li><li>шаблон «провайдер»;</li><li>компоненты высшего порядка и т.д.</li></ul><p>Есть очень хороший ресурс для начала изучения шаблонов проектирования. Тут 22 шаблона с иллюстрациями и примерами.</p><p>Для лучшего понимания шаблонов проектирования полезно освежить в голове принципы Объектно-ориентированного программирования (ООП), а также копнуть в сторону функционального программирования.</p><h2>Итак, какие шаблоны проектирования нужно знать и действительно ли нужно?</h2><p>Стоит помнить, что если вы новичок в программировании, то не стоит зацикливаться на шаблонах. Да, их полезно знать, но лучше сначала набраться опыта, чем прочитать про один-два шаблона и пытаться их всюду применить, не понимая сути.</p><p>Шаблоны проектирования отличаются в разных экосистемах. Поэтому вместо того чтобы искать универсальные решения, уделите внимание шаблонам, принятым в вашей.</p><p>Кроме того, для решения задачи знания подходящих шаблонов проектирования недостаточно. Не помешает изучить анти-шаблоны — подходы, которые приводят к плохим результатам.</p><p>Некоторые эксперты сошлись в том, что если и изучать шаблоны проектирования, то начинать стоит с простых, вроде фабрики, фабричного метода, фасада и т. д. Также в первую очередь стоит смотреть в сторону порождающих и структурных шаблонов.</p><p>И вообще, изучайте те шаблоны, которые будут помогать решать ваши ежедневные задачи.</p><p>Напоминаем, что вы можете <a href="https://docs.google.com/forms/d/e/1FAIpQLSdSanNvlfPRrSyQWfnoGPflSVwO4KctnjOdEKHzuxjCmFX2dA/viewform">задать свой вопрос</a> экспертам, а мы соберём на него ответы, если он окажется интересным. Вопросы, которые уже задавались, можно найти в списке выпусков <a href="https://tproger.ru/experts/">рубрики</a>. Если вы хотите присоединиться к числу экспертов и прислать ответ от вашей компании или лично от вас, то пишите на <a>experts@tproger.ru</a>, мы расскажем, как это сделать.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как с помощью принципа единственной ответственности писать гибкий и модульный код</title>
      <link>https://tproger.ru/translations/solid-srp-explained</link>
      <comments>https://tproger.ru/translations/solid-srp-explained?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Туренко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/solid-srp-explained</guid>
      <description><![CDATA[<p>SRP из набора принципов SOLID: как идея Роберта Мартина помогает писать чистый, хорошо структурированный и легко читаемый код.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/solid-srp-explained">Как с помощью принципа единственной ответственности писать гибкий и модульный код</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Для продолжающих]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 01 Mar 2019 22:22:57 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы занимались разработкой ПО, вам наверняка знакома аббревиатура SOLID.</p><p>Это свод принципов, призванный помочь разработчикам писать чистый, хорошо структурированный и легко читаемый код. Программисты представляют себе по-разному «правильный» подход к написанию приложений — это больше зависит от их личного опыта и предпочтений. Однако идеи SOLID широко распространены среди разработчиков ПО, более того, они были интегрированы в Agile.</p><p>Расшифровка аббревиатуры:</p><ul><li>Single Responsibility Principle (SRP) — принцип единственной ответственности. Каждый класс выполняет только свои задачи.</li><li>Open-Closed Principle (OCP) — принцип открытости/закрытости — программные сущности (классы, модули, функции и пр.) должны быть открыты для расширения, но закрыты для модификации.</li><li>Liskov Substitution Principle (LSP) — принцип подстановки Барбары Лисков гласит: «наследующий класс должен дополнять, а не изменять базовый».</li><li>Interface Segregation Principle (ISP) — принцип разделения интерфейса: «много интерфейсов, специально предназначенных для клиентов, лучше, чем один интерфейс общего назначения».</li><li>Dependency Inversion Principle (DIP) — принцип инверсии зависимостей: «абстракции не должны зависеть от деталей — детали должны зависеть от абстракций».</li></ul><p>В данном материале мы рассмотрим SRP — принцип единственной ответственности.</p><h2>Немного предыстории</h2><p>Роберт Мартин изначально <a href="https://web.archive.org/web/20150202200348/http://www.objectmentor.com/resources/articles/srp.pdf">ввёл</a> термин в качестве составляющей своего труда «Принципы объектно-ориентированного проектирования». В основу SRP Мартина легла закономерность связности, описанная Томом Демарко и Мейлиром Пейдж-Джонсоном.</p><p>Кроме того, в разработке ПО есть два схожих понятия – инкапсуляция и сокрытие информации. SRP включает в себя также и эти два (или одно) понятия от Дэвида Парнаса, который <a href="https://prl.ccs.neu.edu/img/p-tr-1971.pdf">обозначил</a> их примерно так: «декомпозиция системы на модули не должна основываться на анализе блок-схем или потоков исполнения. Вместо этого, каждый модуль должен содержать внутри некоторое решение (design decision), предоставляя минимальное количество информации о нём своим клиентам».</p><h2>Суть SRP</h2><p>Суть SRP в одном предложении: «соберите всё, изменяемое по одной и той же причине, но разделите изменяемое по разным причинам».</p><p>Если разные люди работают с одной и той же программой, то изменение части, с которой взаимодействует один человек, не должно влиять на ту часть, с которой работает другой.</p><h2>«Божественный объект»</h2><figure><img src="https://media.tproger.ru/user-uploads/34561/2023-12-26/74e51175-6704-4f95-af39-052f6cc079d9.gif" alt="" /><figcaption>Классы обращаются к божественному объекту</figcaption></figure><p>Лучший способ изучить SRP — увидеть его в действии. Ниже показан пример программы на Ruby, не соответствующей принципу единственной ответственности. Код описывает поведение и атрибуты космической станции. Посмотрите на него и попробуйте определить:</p><ul><li>обязанности объектов, конкретизированные в классе SpaceStation;</li><li>виды лиц, которые могут быть заинтересованы в деятельности космической станции.</li></ul><p>Видно, что класс SpaceStation имеет несколько «функций» или «задач»:</p><ul><li>работа с сенсорами;</li><li>использование расходных материалов;</li><li>расход топлива;</li><li>использование подруливающих двигателей.</li></ul><p>Субъекты не указаны в коде, но можно предположить, что это:</p><ul><li>научный работник, управляющий сенсорами;</li><li>специалист по материально-техническому обеспечению;</li><li>лицо, ответственное за запасы топлива;</li><li>пилот.</li></ul><p>Программа полностью не соответствует принципам SRP, но в полной мере отражает суть <a 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">«Божественного объекта»</a> — основного анти-шаблона в объектно-ориентированном программировани. При таком подходе объект хранит слишком большое количество данных и содержит много методов, поэтому его роль становится «божественной» или всеобъемлющей. Вместо того, чтобы общаться друг с другом, объекты обращаются к всеобъемлющему, а так как на нём завязан весь проект (или его большая часть), то его обслуживание усложняется, увеличивая риск поломки существующей функциональности.</p><p>Обратимся к примеру с космической станцией. Представьте, если надо добавить медицинский отсек, а из-за этого произойдут какие-нибудь проблемы с топливным. Попробуйте представить, что для того, чтобы шагнуть, вам нужно выгнуть левую руку назад, повернуть голову вправо и нагнуться. Работает? Да. Терпимо? Возможно. А если нужно бежать? А если с вёдрами? То же самое и с проектом — он будет работать, но до определённого момента.</p><p>Нарушение SRP может быть выгодно в начале проекта, но это лишь краткосрочно. С ростом проекта будут увеличиваться финансовые и временны́е ресурсы для исправления существующих проблем. Влияющие друг на друга участки кода, его громоздкость и нечитаемость — главные проблемы, о которых надо подумать при игнорировании SRP.</p><h2>Разбивка по обязанностям</h2><p>Выше были определены 4 функции станции, которые управлялись классом SpaceStation. Они и будут отправной точкой рефакторинга кода. Теперь программа чуть больше соответствует SRP.</p><p>Теперь класс SpaceStation скорее служит контейнером, внутри которого выполняются операции для подчинённых частей:</p><ul><li>набора сенсоров;</li><li>системы подачи расходных материалов;</li><li>топливного бака;</li><li>подруливающих двигателей.</li></ul><p>Каждая из частей принимает форму поля, задаваемого при инициализации космической станции. Для каждой переменной есть соответствующий класс:</p><ul><li>Sensors (сенсоры);</li><li>SupplyHold (поставки расходных материалов);</li><li>FuelTank (топливный бак);</li><li>Thrusters (подруливающие двигатели).</li></ul><p>В этой версии кода есть некоторые важные отличия от предыдущей, а именно: отдельные элементы функциональности не только инкапсулированы в собственные классы, но и организованы таким образом, чтобы быть предсказуемыми и последовательными. Идея заключается в группировке сходных по функциональности элементов, для следования принципу связности, и в изолировании данных таким образом, чтобы они были доступны только для соответствующих субъектов. Если понадобится изменить принцип работы системы поставки с хэш-структуры на массив, то это легко можно сделать, используя класс SupplyHold. Таким образом другие модули не будут затронуты. Причём класс SpaceStation даже не будет догадываться о действиях, производимых в модулях.</p><p>Заметьте, что сейчас в коде выше есть методы report_supplies и report_fuel, содержащиеся в классах SupplyHold и FuelTank. Что, если с Земли попросят изменить механизм загрузки отчётов? Придётся редактировать оба предыдущих класса. А если руководство решит изменить технологию доставки топлива и расходных материалов? Кажется, придётся повторно изменять те же классы. Похоже на нарушение SRP. Надо бы исправить.</p><p>В последней версии программы обязанности «модулей» были разбиты на два дочерних класса FuelReporter и SupplyReporter, объединённых под родительским классом Reporter. Далее были добавлены экземплярные переменные к классу SpaceStation, чтобы запустить соответствующий Reporter. Если руководству с Земли понадобится ещё что-то изменить, то можно внести правки в подклассы, не влияя на работу объектов (классов), о которых они докладывают.</p><p>Конечно, до сих пор есть некоторая связь между разными классами. Например, SupplyReporter зависит от SupplyHold, так же зависим и FuelReporter от FuelTank. Кроме того, подруливающие двигатели тоже должны быть связаны с топливным баком. Все эти связи кажутся довольно логичными и на этом уровне уже можно изменять код одного объекта, не влияя на другой (либо влияя незначительно).</p><p>В итоге код программы стал более «модульным» и обязанности объектов ясным образом были обозначены. Вероятность «поломки» кода значительно уменьшена, а работать с ним стало приятнее, так как «божественный объект» (которым был весь код до второй редакции) был преобразован в SRP-код, если так можно выразиться.</p>]]></content:encoded>
    </item>
    <item>
      <title>Шаблоны проектирования простым языком. Часть третья. Поведенческие шаблоны</title>
      <link>https://tproger.ru/translations/design-patterns-simple-words-3</link>
      <comments>https://tproger.ru/translations/design-patterns-simple-words-3?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Богдан Федоренко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/design-patterns-simple-words-3</guid>
      <description><![CDATA[<p>Третья статья из цикла, посвящённого шаблонам, или паттернам, проектирования. На понятных примерах объясняем суть поведенческих шаблонов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/design-patterns-simple-words-3">Шаблоны проектирования простым языком. Часть третья. Поведенческие шаблоны</a>»</p>]]></description>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Для продолжающих]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 04 Jul 2017 11:40:07 GMT</pubDate>
      <content:encoded><![CDATA[<p>Рассказывает Камран Ахмед</p><p>Шаблоны проектирования — это руководства по решению повторяющихся проблем. Это не классы, пакеты или библиотеки, которые можно было бы подключить к вашему приложению и сидеть в ожидании чуда. Они скорее являются методиками, как решать определенные проблемы в определенных ситуациях.</p><p>Википедия <a href="https://ru.wikipedia.org/wiki/Шаблон_проектирования">описывает</a> их следующим образом:</p><blockquote><b>Шаблон проектирования</b> или <b>паттерн</b>,в разработке программного обеспечения— повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования, врамках некоторого часто возникающего контекста.</blockquote><h3>Будьте осторожны</h3><ul><li>шаблоны проектирования не являются решением всех ваших проблем;</li><li>не пытайтесь насильно использовать их, из-за этого могут произойти плохие вещи. Шаблоны — решения проблем, а не решения для поиска проблем;</li><li>если их правильно использовать в нужных местах, то они могут стать спасением, а иначе могут привести к ужасному беспорядку.</li></ul><p>Также заметьте, что примеры ниже написаны на PHP 7. Но это не должно вас останавливать, ведь принципы остаются такими же.</p><h3>Типы шаблонов</h3><p>Шаблоны бывают следующих трех видов:</p><ol><li><a href="https://tproger.ru/translations/design-patterns-simple-words-1/">Порождающие</a>.</li><li><a href="https://tproger.ru/translations/design-patterns-simple-words-2/">Структурные</a>.</li><li>Поведенческие — о них мы рассказываем в этой статье.</li></ol><p>Простыми словами: Поведенческие шаблоны связаны с распределением обязанностей между объектами. Их отличие от структурных шаблонов заключается в том, что они не просто описывают структуру, но также описывают шаблоны для передачи сообщений / связи между ними. Или, другими словами, они помогают ответить на вопрос «Как запустить поведение в программном компоненте?»</p><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F">гласит</a>:</p><blockquote><b>Поведенческие шаблоны</b>— шаблоны проектирования, определяющие алгоритмы испособы реализации взаимодействия различных объектов иклассов.</blockquote><p>Поведенческие шаблоны:</p><ul><li><a href="https://tproger.ru/#31">цепочка обязанностей (Chain of Responsibility)</a>;</li><li><a href="https://tproger.ru/#32">команда (Command)</a>;</li><li><a href="https://tproger.ru/#33">итератор (Iterator)</a>;</li><li><a href="https://tproger.ru/#34">посредник (Mediator)</a>;</li><li><a href="https://tproger.ru/#35">хранитель (Memento)</a>;</li><li><a href="https://tproger.ru/#36">наблюдатель (Observer)</a>;</li><li><a href="https://tproger.ru/#37">посетитель (Visitor)</a>;</li><li><a href="https://tproger.ru/#38">стратегия (Strategy)</a>;</li><li><a href="https://tproger.ru/#39">состояние (State)</a>;</li><li><a href="https://tproger.ru/#30">шаблонный метод (Template Method)</a>.</li></ul><h2>Цепочка обязанностей (Chain of Responsibility)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%A6%D0%B5%D0%BF%D0%BE%D1%87%D0%BA%D0%B0_%D0%BE%D0%B1%D1%8F%D0%B7%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B5%D0%B9">гласит</a>:</p><blockquote><b>Цепочка обязанностей</b>— поведенческий шаблон проектирования предназначенный для организации всистеме уровней ответственности.</blockquote><p>Пример из жизни: например, у вас есть три платежных метода (A, B и C), настроенных на вашем банковском счёте. На каждом лежит разное количество денег. На A есть 100 долларов, на B есть 300 долларов и на C — 1000 долларов. Предпочтение отдается в следующем порядке: A, B и C. Вы пытаетесь заказать что-то, что стоит 210 долларов. Используя цепочку обязанностей, первым на возможность оплаты будет проверен метод А, и в случае успеха пройдет оплата и цепь разорвется. Если нет, то запрос перейдет к методу B для аналогичной проверки. Здесь A, B и C — это звенья цепи, а все явление — цепочка обязанностей.</p><p>Простыми словами: цепочка обязанностей помогает строить цепочки объектов. Запрос входит с одного конца и проходит через каждый объект, пока не найдет подходящий обработчик.</p><p>Обратимся к коду. Приведем пример с банковскими счетами. Изначально у нас есть базовый Account с логикой для соединения счетов цепью и некоторые счета:</p><p>Теперь приготовим цепь, используя объявленные выше звенья (например, Bank, Paypal, Bitcoin):</p><p>Примеры на Java и Python.</p><h2>Команда (Command)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Команда</b>— поведенческий шаблон проектирования, используемый при объектно-ориентированном программировании, представляющий действие. Объект команды заключает всебе само действие иего параметры.</blockquote><p>Пример из жизни: Типичный пример: вы заказываете еду в ресторане. Вы (т.е. Client) просите официанта (например, Invoker) принести еду (то есть Command), а официант просто переправляет запрос шеф-повару (то есть Receiver), который знает, что и как готовить. Другим примером может быть то, что вы (Client) включаете (Command) телевизор (Receiver) с помощью пульта дистанционного управления (Invoker).</p><p>Простыми словами: Позволяет вам инкапсулировать действия в объекты. Основная идея, стоящая за шаблоном — это предоставление средств, для разделения клиента и получателя.</p><p>Обратимся к коду. Изначально у нас есть получатель Bulb, в котором есть реализация каждого действия, которое может быть выполнено:</p><p>Затем у нас есть интерфейс Command, который каждая команда должна реализовывать, и затем у нас будет набор команд:</p><p>Затем у нас есть Invoker, с которым клиент будет взаимодействовать для обработки любых команд:</p><p>Наконец, мы можем увидеть, как использовать нашего клиента:</p><p>Шаблон команда может быть использован для реализации системы, основанной на транзакциях, где вы сохраняете историю команд, как только их выполняете. Если окончательная команда успешно выполнена, то все хорошо, иначе алгоритм просто перебирает историю и продолжает выполнять отмену для всех выполненных команд.</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/command">Java </a>и Python.</p><h2>Итератор (Iterator)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%98%D1%82%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Итератор</b>— поведенческий шаблон проектирования. Представляет собой объект, позволяющий получить последовательный доступ кэлементам объекта-агрегата без использования описаний каждого изагрегированных объектов.</blockquote><p>Пример из жизни: Старый радионабор будет хорошим предметом итератора, где пользователь может начать искать сигнал на каком-то канале и затем использовать кнопки переключения на следующий и предыдущий канал для перехода между соответствующими каналами. Или используем пример телевизора, где вы можете нажимать кнопки следующего или предыдущего канала для перехода через последовательные каналы, или, иными словами, они предоставляют интерфейс для итерирования между соответствующими каналами, песнями или радиостанциями.</p><p>Простыми словами: Представляет способ доступа к элементам объекта без показа базового представления.</p><p>Обратимся к примерам в коде. В PHP очень просто реализовать это, используя SPL (Standard PHP Library). Приводя наш пример с радиостанциями, изначально у нас есть Radiostation:</p><p>Затем у нас есть итератор:</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/iterator">Java </a>и Python.</p><h2>Посредник (Mediator)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B8%D0%BA_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Посредник</b> — поведенческий шаблон проектирования, обеспечивающий взаимодействие множества объектов, формируя при этом слабую связанность, иизбавляя объекты, отнеобходимости явно ссылаться друг надруга.</blockquote><p>Пример из жизни: Общим примером будет, когда вы говорите с кем-то по мобильнику, то между вами и собеседником находится мобильный оператор. То есть сигнал передаётся через него, а не напрямую. В данном примере оператор — посредник.</p><p>Простыми словами: Шаблон посредник подразумевает добавление стороннего объекта (посредника) для управления взаимодействием между двумя объектами (коллегами). Шаблон помогает уменьшить связанность (coupling) классов, общающихся друг с другом, ведь теперь они не должны знать о реализациях своих собеседников.</p><p>Разберем пример в коде. Простейший пример: чат (посредник), в котором пользователи (коллеги) отправляют друг другу сообщения.</p><p>Изначально у нас есть посредник ChatRoomMediator:</p><p>Затем у нас есть наши User (коллеги):</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/mediator">Java </a>и Python.</p><h2>Хранитель (Memento)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%A5%D1%80%D0%B0%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Хранитель</b> — поведенческий шаблон проектирования, позволяющий, ненарушая инкапсуляцию, зафиксировать исохранить внутреннее состояние объекта так, чтобы позднее восстановить его вэтом состоянии.</blockquote><p>Пример из жизни: В качестве примера можно привести калькулятор (создатель), у которого любая последняя выполненная операция сохраняется в памяти (хранитель), чтобы вы могли снова вызвать её с помощью каких-то кнопок (опекун).</p><p>Простыми словами: Шаблон хранитель фиксирует и хранит текущее состояние объекта, чтобы оно легко восстанавливалось.</p><p>Обратимся к коду. Возьмем наш пример текстового редактора, который время от времени сохраняет состояние, которое вы можете восстановить.</p><p>Изначально у нас есть наш объект EditorMemento, который может содержать состояние редактора:</p><p>Затем у нас есть наш Editor (создатель), который будет использовать объект хранитель:</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/memento">Java </a>и Python.</p><h2>Наблюдатель (Observer)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D0%B1%D0%BB%D1%8E%D0%B4%D0%B0%D1%82%D0%B5%D0%BB%D1%8C_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Наблюдатель</b> — поведенческий шаблон проектирования, также известен как «подчинённые» (Dependents). Создает механизм укласса, который позволяет получать экземпляру объекта этого класса оповещения отдругих объектов обизменении ихсостояния, тем самым наблюдая заними.</blockquote><p>Пример из жизни: Хороший пример: люди, ищущие работу, подписываются на публикации на сайтах вакансий и получают уведомления, когда появляются вакансии подходящие по параметрам.</p><p>Простыми словами: Шаблон определяет зависимость между объектами, чтобы при изменении состояния одного из них зависимые от него узнавали об этом.</p><p>Обратимся к коду. Приводя наш пример. Изначально у нас есть JobSeeker, которые ищут работы JobPost и должны быть уведомлены о её появлении:</p><p>Затем мы делаем публикации JobPostings на которые соискатели могут подписываться:</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/observer">Java </a>и Python.</p><h2>Посетитель (Visitor)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%81%D0%B5%D1%82%D0%B8%D1%82%D0%B5%D0%BB%D1%8C_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Посетитель</b> — поведенческий шаблон проектирования, описывающий операцию, которая выполняется над объектами других классов. При изменении visitor нет необходимости изменять обслуживаемые классы.</blockquote><p>Пример из жизни: Туристы собрались в Дубай. Сначала им нужен способ попасть туда (виза). После прибытия они будут посещать любую часть города, не спрашивая разрешения ходить где вздумается. Просто скажите им о каком-нибудь месте — и туристы могут там побывать. Шаблон посетитель помогает добавлять места для посещения.</p><p>Простыми словами: Шаблон посетитель позволяет добавлять будущие операции для объектов без их модифицирования.</p><p>Перейдем к примерам в коде. Возьмём зоопарк: у нас есть несколько видов Animal, и нам нужно послушать издаваемые ими звуки.</p><p>Затем у нас есть реализация для животных:</p><p>Давайте реализуем посетителя:</p><p>Пример использования:</p><p>Это можно было сделать просто с помощью иерархии наследования, но тогда пришлось бы модифицировать животных при каждом добавлении к ним новых действий. А здесь менять их не нужно. Например, мы можем добавить животным прыжки, просто создав нового посетителя:</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/visitor">Java </a>и Python.</p><h2>Стратегия (Strategy)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D0%B0%D1%82%D0%B5%D0%B3%D0%B8%D1%8F_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Стратегия</b> — поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляции каждого изних иобеспечения ихвзаимозаменяемости. Это позволяет выбирать алгоритм путём определения соответствующего класса. Шаблон Strategy позволяет менять выбранный алгоритм независимо отобъектов-клиентов, которые его используют.</blockquote><p>Пример из жизни: Возьмём пример с пузырьковой сортировкой. Мы её реализовали, но с ростом объёмов данных сортировка работа стала выполняться очень медленно. Тогда мы сделали быструю сортировку. Алгоритм работает быстрее на больших объёмах, но на маленьких он очень медленный. Тогда мы реализовали стратегию, при которой для маленьких объёмов данных используется пузырьковая сортировка, а для больших объёмов — быстрая.</p><p>Простыми словами: Шаблон стратегия позволяет переключаться между алгоритмами или стратегиями в зависимости от ситуации.</p><p>Перейдем к коду. Возьмем наш пример. Изначально у нас есть наша SortStrategy и разные её реализации:</p><p>И у нас есть Sorter, который собирается использовать какую-то стратегию:</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/strategy">Java </a>и Python.</p><h2>Состояние (State)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Состояние</b> — поведенческий шаблон проектирования. Используется втех случаях, когда вовремя выполнения программы объект должен менять своё поведение взависимости отсвоего состояния.</blockquote><p>Пример из жизни: Допустим, в графическом редакторе вы выбрали кисть. Она меняет своё поведение в зависимости от настройки цвета, т. е. рисует линию выбранного цвета.</p><p>Простыми словами: Шаблон позволяет менять поведение класса при изменении состояния.</p><p>Перейдем к примерам в коде. Возьмем пример текстового редактора, он позволяет вам менять состояние напечатанного текста. Например, если у вас выбран курсив, то он будет писать курсивом и так далее.</p><p>Изначально у нас есть интерфейс WritingState и несколько его реализаций:</p><p>Затем TextEditor:</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/state">Java </a>и Python.</p><p>Паттерн состояние применяется в проектировании распределённых сиситем, наряду с <a href="https://tproger.ru/translations/top-5-arhitekturnyh-patternov-dlja-raspredeljonnyh-sistem/">другими паттернами</a>.</p><h2>Шаблонный метод (Template Method)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Шаблонный метод</b> — поведенческий шаблон проектирования, определяющий основу алгоритма ипозволяющий наследникам переопределять некоторые шаги алгоритма, неизменяя его структуру вцелом.</blockquote><p>Пример из жизни: Допустим, вы собрались строить дома. Этапы будут такими:</p><ol><li>Подготовка фундамента.</li><li>Возведение стен.</li><li>Настил крыши.</li><li>Настил перекрытий.</li></ol><p>Порядок этапов никогда не меняется. Вы не настелите крышу до возведения стен и т. д. Но каждый этап модифицируется: стены, например, можно возвести из дерева, кирпича или газобетона.</p><p>Простыми словами: Шаблонный метод определяет каркас выполнения определённого алгоритма, но реализацию самих этапов делегирует дочерним классам.</p><p>Обратимся к коду. Допустим, у нас есть программный инструмент, позволяющий тестировать, проводить контроль качества кода, выполнять сборку, генерировать отчёты сборки (отчёты о покрытии кода, о качестве кода и т. д.), а также развёртывать приложение на тестовом сервере.</p><p>Изначально у нас есть наш Builder, который описывает скелет для построения алгоритма:</p><p>Затем у нас есть его реализации:</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/template-method">Java </a>и Python.</p>]]></content:encoded>
    </item>
    <item>
      <title>Шаблоны проектирования простым языком. Часть вторая. Структурные шаблоны</title>
      <link>https://tproger.ru/translations/design-patterns-simple-words-2</link>
      <comments>https://tproger.ru/translations/design-patterns-simple-words-2?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Богдан Федоренко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/design-patterns-simple-words-2</guid>
      <description><![CDATA[<p>Вторая статья из цикла, посвящённого шаблонам, или паттернам, проектирования. На понятных примерах объясняем суть структурных шаблонов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/design-patterns-simple-words-2">Шаблоны проектирования простым языком. Часть вторая. Структурные шаблоны</a>»</p>]]></description>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Для продолжающих]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 04 Jul 2017 11:34:53 GMT</pubDate>
      <content:encoded><![CDATA[<p>Рассказывает Камран Ахмед</p><p>Шаблоны проектирования — это руководства по решению повторяющихся проблем. Это не классы, пакеты или библиотеки, которые можно было бы подключить к вашему приложению и сидеть в ожидании чуда. Они скорее являются методиками решения определенных проблем в определенных ситуациях.</p><p>Википедия <a href="https://ru.wikipedia.org/wiki/Шаблон_проектирования">описывает</a> их следующим образом:</p><blockquote><b>Шаблон проектирования</b>или <b>паттерн</b>,в разработке программного обеспечения— повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования, врамках некоторого часто возникающего контекста.</blockquote><h3>Будьте осторожны</h3><ul><li>шаблоны проектирования не являются решением всех ваших проблем;</li><li>не пытайтесь использовать их в обязательном порядке — это может привести к негативным последствиям. Шаблоны — это подходы к решению проблем, а не решения для поиска проблем;</li><li>если их правильно использовать в нужных местах, то они могут стать спасением, а иначе могут привести к ужасному беспорядку.</li></ul><p>Также заметьте, что примеры ниже написаны на PHP 7. Но это не должно вас останавливать, ведь принципы остаются такими же.</p><h3>Типы шаблонов</h3><p>Шаблоны бывают следующих трех видов:</p><ol><li><a href="https://tproger.ru/translations/design-patterns-simple-words-1/">Порождающие</a>.</li><li>Структурные — о них мы рассказываем в этой статье.</li><li><a href="https://tproger.ru/translations/design-patterns-simple-words-3/">Поведенческие</a>.</li></ol><p>Простыми словами: Структурные шаблоны в основном связаны с композицией объектов, другими словами, с тем, как сущности могут использовать друг друга. Ещё одним объяснением было бы то, что они помогают ответить на вопрос «Как создать программный компонент?».</p><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%BD%D1%8B%D0%B5_%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F">гласит</a>:</p><blockquote><b>Структурные шаблоны</b> — шаблоны проектирования, вкоторых рассматривается вопрос отом, как изклассов иобъектов образуются более крупные структуры.</blockquote><p>Список структурных шаблонов проектирования:</p><ul><li><a href="https://tproger.ru/#21">адаптер (Adapter)</a>;</li><li><a href="https://tproger.ru/#22">мост (Bridge)</a>;</li><li><a href="https://tproger.ru/#23">компоновщик (Composite)</a>;</li><li><a href="https://tproger.ru/#24">декоратор (Decorator)</a>;</li><li><a href="https://tproger.ru/#25">фасад (Facade)</a>;</li><li><a href="https://tproger.ru/#26">приспособленец (Flyweight)</a>;</li><li><a href="https://tproger.ru/#27">заместитель (Proxy)</a>.</li></ul><h2>Адаптер (Adapter)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%90%D0%B4%D0%B0%D0%BF%D1%82%D0%B5%D1%80_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Адаптер</b> — структурный шаблон проектирования, предназначенный для организации использования функций объекта, недоступного для модификации, через специально созданный интерфейс.</blockquote><p>Пример из жизни: Представим, что у вас на карте памяти есть какие-то изображения и вам надо перенести их на ваш компьютер. Чтобы это сделать, вам нужен какой-то адаптер, который совместим с портами вашего компьютера. В этом случае карт-ридер — это адаптер. Другим примером будет блок питания. Вилку с тремя ножками нельзя вставить в розетку с двумя отверстиями. Для того, чтобы она подошла, надо использовать адаптер. Ещё одним примером будет переводчик, переводящий слова одного человека для другого.</p><p>Простыми словами: Шаблон позволяет обернуть несовместимые объекты в адаптер, чтобы сделать их совместимыми с другим классом.</p><p>Обратимся к коду. Представим игру, в которой охотник охотится на львов.</p><p>Изначально у нас есть интерфейс Lion, который реализует всех львов:</p><p>И Hunter охотится на любую реализацию интерфейса Lion:</p><p>Теперь представим, что нам надо добавить WildDog в нашу игру, на которую наш Hunter также мог бы охотиться. Но мы не можем сделать это напрямую, потому что у WildDog другой интерфейс. Чтобы сделать её совместимой с нашим Hunter, нам надо создать адаптер:</p><p>Способ применения:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/adapter">Java</a> и Python.</p><h2>Мост (Bridge)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D1%81%D1%82_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Мост</b> — структурный шаблон проектирования, используемый впроектировании программного обеспечения чтобы разделять абстракцию иреализацию так, чтобы они могли изменяться независимо. Шаблон мост использует инкапсуляцию, агрегирование иможет использовать наследование для того, чтобы разделить ответственность между классами.</blockquote><p>Пример из жизни: Представим, что у вас есть сайт с разными страницами, и вам надо разрешить пользователям менять их тему. Что вы будете делать? Создавать множественные копии каждой страницы для каждой темы или просто отдельную тему, которую пользователь сможет выбрать сам? Шаблон мост позволяет вам сделать второе.</p><p><a href="https://media.tproger.ru/uploads/2017/05/bridge.png"></a></p><p>Простыми словами: Шаблон мост — это предпочтение композиции над наследованием. Детали реализации передаются из одной иерархии в другой объект с отдельной иерархией.</p><p>Обратимся к примеру в коде. Возьмем пример с нашими страницами. У нас есть иерархия WebPage:</p><p>И отдельная иерархия Theme:</p><p>Применение в коде:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/bridge">Java</a> и Python.</p><h2>Компоновщик (Composite)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%BE%D0%B2%D1%89%D0%B8%D0%BA_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Компоновщик</b> — структурный шаблон проектирования, объединяющий объекты вдревовидную структуру для представления иерархии отчастного кцелому. Компоновщик позволяет клиентам обращаться котдельным объектам икгруппам объектов одинаково. Паттерн определяет иерархию классов, которые одновременно могут состоять изпримитивных исложных объектов, упрощает архитектуру клиента, делает процесс добавления новых видов объекта более простым.</blockquote><p>Пример из жизни: Каждая организация скомпонована из сотрудников. У каждого сотрудника есть одинаковые свойства, такие как зарплата, обязанности, отчётность и т.д.</p><p>Простыми словами: Шаблон компоновщик позволяет клиентам работать с индивидуальными объектами в едином стиле.</p><p>Обратимся к коду. Возьмем наш пример с рабочими. У нас есть Employee разных типов:</p><p>Теперь у нас есть TaskManager:</p><p>Способ применения:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/composite">Java</a> и Python.</p><h2>Декоратор (Decorator)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BA%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Декоратор</b> — структурный шаблон проектирования, предназначенный для динамического подключения дополнительного поведения кобъекту. Шаблон декоратор предоставляет гибкую альтернативу практике создания подклассов сцелью расширения функциональности.</blockquote><p>Пример из жизни: Представим, что у вас есть свой автосервис. Как вы будете рассчитывать сумму в счете за услуги? Вы выбираете одну услугу и динамически добавляете к ней цены на предоставляемые услуги, пока не получите окончательную стоимость. Здесь каждый тип сервиса является декоратором.</p><p>Простыми словами: Шаблон декоратор позволяет вам динамически изменять поведение объекта во время работы, оборачивая их в объект класса декоратора.</p><p>Перейдем к коду. Возьмем пример с кофе. Изначально у нас есть простой Coffee и реализующий его интерфейс:</p><p>Мы хотим сделать код расширяемым, чтобы при необходимости можно было изменять его. Давайте сделаем некоторые дополнения (декораторы):</p><p>А теперь приготовим Coffee:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/decorator">Java</a> и Python.</p><h2>Фасад (Facade)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%A4%D0%B0%D1%81%D0%B0%D0%B4_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Фасад</b> — структурный шаблон проектирования, позволяющий скрыть сложность системы путём сведения всех возможных внешних вызовов кодному объекту, делегирующему ихсоответствующим объектам системы.</blockquote><p>Пример из жизни: Как вы включаете компьютер? Нажимаю на кнопку включения, скажете вы. Это то, во что вы верите, потому что вы используете простой интерфейс, который компьютер предоставляет для доступа снаружи. Внутри же должно произойти гораздо больше вещей. Этот простой интерфейс для сложной подсистемы называется фасадом.</p><p>Простыми словами: Шаблон фасад предоставляет упрощенный интерфейс для сложной системы.</p><p>Перейдем к примерам в коде. Возьмем пример с компьютером. Изначально у нас есть класс Computer:</p><p>Затем у нас есть фасад:</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/facade">Java</a> и Python.</p><h2>Приспособленец (Flyweight)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D1%81%D0%BF%D0%BE%D1%81%D0%BE%D0%B1%D0%BB%D0%B5%D0%BD%D0%B5%D1%86_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Приспособленец</b> — структурный шаблон проектирования, при котором объект, представляющий себя как уникальный экземпляр вразных местах программы, пофакту неявляется таковым.</blockquote><p>Пример из жизни: Вы когда-нибудь заказывали чай в уличном ларьке? Там зачастуют готовят не одну чашку, которую вы заказали, а гораздо большую емкость. Это делается для того, чтобы экономить ресурсы (газ/электричество). Газ/электричество в этом примере и являются приспособленцами, ресурсы которых делятся (sharing).</p><p>Простыми словами: Приспособленец используется для минимизации использования памяти или вычислительной стоимости путем разделения ресурсов с наибольшим количеством похожих объектов.</p><p>Перейдем к примерам в коде. Возьмем наш пример с чаем. Изначально у нас есть различные виды Tea и TeaMaker:</p><p>Теперь у нас есть TeaShop, который принимает заказы и выполняет их:</p><p>Пример использования:</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/flyweight">Java</a> и Python.</p><h2>Заместитель (Proxy)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/Proxy_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Заместитель</b> — структурный шаблон проектирования, который предоставляет объект, который контролирует доступ кдругому объекту, перехватывая все вызовы (выполняет функцию контейнера).</blockquote><p>Пример из жизни: Вы когда-нибудь использовали карту доступа, чтобы пройти через дверь? Есть несколько способов открыть дверь: например, она может быть открыта при помощи карты доступа или нажатия кнопки, которая обходит защиту. Основная функциональность двери — это открытие, но заместитель, добавленный поверх этого, добавляет функциональность. Но лучше я объясню это на примере кода чуть ниже.</p><p>Простыми словами: Используя шаблон заместитель, класс отображает функциональность другого класса.</p><p>Перейдем к коду. Возьмем наш пример с безопасностью. Сначала у нас есть интерфейс Door и его реализация:</p><p>Затем у нас есть заместитель Security для защиты любых наших дверей:</p><p>Пример использования:</p><p>Другим примером будет реализация маппинга данных. Например, недавно я создал ODM (Object Data Mapper) для MongoDB, используя этот шаблон, где я написал заместитель вокруг классов mongo и использовал магический метод __call(). Все вызовы методов были замещены оригинальным классом mongo, и полученный результат возвращался без изменений, но в случае find или findOne данные сопоставлялись необходимому классу, и возвращались в объект вместо Cursor.</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/proxy">Java </a>и Python.</p>]]></content:encoded>
    </item>
    <item>
      <title>Шаблоны проектирования простым языком. Часть первая. Порождающие шаблоны</title>
      <link>https://tproger.ru/translations/design-patterns-simple-words-1</link>
      <comments>https://tproger.ru/translations/design-patterns-simple-words-1?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Богдан Федоренко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/design-patterns-simple-words-1</guid>
      <description><![CDATA[<p>Разбираем порождающие паттерны проектирования: Simple Factory, Factory Method, Abstract Factory, Builder, Prototype и Singleton. Примеры кода на PHP с объяснениями.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/design-patterns-simple-words-1">Шаблоны проектирования простым языком. Часть первая. Порождающие шаблоны</a>»</p>]]></description>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Для продолжающих]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 01 Jun 2017 09:14:20 GMT</pubDate>
      <content:encoded><![CDATA[<p>Рассказывает Камран Ахмед</p><p>Шаблоны проектирования — это руководства по решению повторяющихся проблем. Это не классы, пакеты или библиотеки, которые можно было бы подключить к вашему приложению и сидеть в ожидании чуда. Они скорее являются методиками, как решать определенные проблемы в определенных ситуациях.</p><p>Википедия <a href="https://ru.wikipedia.org/wiki/Шаблон_проектирования">описывает</a> их следующим образом:</p><blockquote><b>Шаблон проектирования</b>, или <b>паттерн</b>,в разработке программного обеспечения— повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования, врамках некоторого часто возникающего контекста.</blockquote><p><b>Ключевые выводы:</b><br />— Порождающие шаблоны решают проблемы создания объектов: от простой фабрики до сложного строителя<br />— Simple Factory и Factory Method делегируют создание объектов, но на разных уровнях абстракции<br />— Abstract Factory группирует связанные фабрики в единый интерфейс<br />— Builder спасает от телескопического конструктора при сложной конфигурации объекта<br />— Prototype клонирует существующие объекты вместо создания с нуля<br />— Singleton гарантирует единственный экземпляр класса, но считается антипаттерном при злоупотреблении</p><h3>Будьте осторожны</h3><ul><li>шаблоны проектирования не являются решением всех ваших проблем;</li><li>не пытайтесь использовать их в обязательном порядке — это может привести к негативным последствиям. Шаблоны — это подходы к решению проблем, а не решения для поиска проблем;</li><li>если их правильно использовать в нужных местах, то они могут стать спасением, а иначе могут привести к ужасному беспорядку.</li></ul><p>Также заметьте, что примеры ниже написаны на PHP 7. Но это не должно вас останавливать, ведь принципы остаются такими же.</p><h3>Типы шаблонов</h3><p>Шаблоны бывают следующих трех видов:</p><ol><li>Порождающие.</li><li><a href="https://tproger.ru/translations/design-patterns-simple-words-2/">Структурные</a>.</li><li><a href="https://tproger.ru/translations/design-patterns-simple-words-3/">Поведенческие</a>.</li></ol><p>Если говорить простыми словами, то это шаблоны, которые предназначены для создания экземпляра объекта или группы связанных объектов.</p><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%80%D0%BE%D0%B6%D0%B4%D0%B0%D1%8E%D1%89%D0%B8%D0%B5_%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F">гласит</a>:</p><blockquote><b>Порождающие шаблоны</b> — шаблоны проектирования, которые абстрагируют процесс инстанцирования. Они позволяют сделать систему независимой отспособа создания, композиции ипредставления объектов. Шаблон, порождающий классы, использует наследование, чтобы изменять наследуемый класс, ашаблон, порождающий объекты, делегирует инстанцирование другому объекту.</blockquote><p>Существуют следующие порождающие шаблоны:</p><ul><li><a href="https://tproger.ru/#simple-factory">простая фабрика (Simple Factory)</a>;</li><li><a href="https://tproger.ru/#factory-method">фабричный метод (Factory Method)</a>;</li><li><a href="https://tproger.ru/#abstract-factory">абстрактная фабрика (Abstract Factory)</a>;</li><li><a href="https://tproger.ru/#builder">строитель (Builder)</a>;</li><li><a href="https://tproger.ru/#prototype">прототип (Prototype)</a>;</li><li><a href="https://tproger.ru/#singleton">одиночка (Singleton)</a>.</li></ul><h2>Простая фабрика (Simple Factory)</h2><p>Википедия <a href="https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)">гласит</a>:</p><blockquote>Вобъектно-ориентированном программировании (ООП), <strong>фабрика</strong>— это объект для создания других объектов. Формально фабрика— это функция или метод, который возвращает объекты изменяющегося прототипа или класса изнекоторого вызова метода, который считается «новым».</blockquote><p>Пример из жизни: Представьте, что вам надо построить дом, и вам нужны двери. Было бы глупо каждый раз, когда вам нужны двери, надевать вашу столярную форму и начинать делать дверь. Вместо этого вы делаете её на фабрике.</p><p>Простыми словами: Простая фабрика генерирует экземпляр для клиента, не раскрывая никакой логики.</p><p>Перейдем к коду. У нас есть интерфейс Door и его реализация:</p><p>Затем у нас есть наша DoorFactory, которая делает дверь и возвращает её:</p><p>И затем мы можем использовать всё это:</p><p>Когда использовать: Когда создание объекта — это не просто несколько присвоений, а какая-то логика, тогда имеет смысл создать отдельную фабрику вместо повторения одного и того же кода повсюду.</p><p><a href="https://github.com/iluwatar/java-design-patterns/tree/master/factory-kit">Пример</a> на Java.</p><h2>Фабричный метод (Factory Method)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%A4%D0%B0%D0%B1%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Фабричный метод</b> — порождающий шаблон проектирования, предоставляющий подклассам интерфейс для создания экземпляров некоторого класса. Вмомент создания наследники могут определить, какой класс создавать. Иными словами, данный шаблон делегирует создание объектов наследникам родительского класса. Это позволяет использовать вкоде программы неспецифические классы, аманипулировать абстрактными объектами наболее высоком уровне.</blockquote><p>Пример из жизни: Рассмотрим пример с менеджером по найму. Невозможно одному человеку провести собеседования со всеми кандидатами на все вакансии. В зависимости от вакансии он должен распределить этапы собеседования между разными людьми.</p><p>Простыми словами: Менеджер предоставляет способ делегирования логики создания экземпляра дочерним классам.</p><p>Перейдём к коду. Рассмотрим приведенный выше пример про HR-менеджера. Изначально у нас есть интерфейс Interviewer и несколько реализаций для него:</p><p>Теперь создадим нашего HiringManager:</p><p>И теперь любой дочерний класс может расширять его и предоставлять необходимого интервьюера:</p><p>Пример использования:</p><p>Когда использовать: Полезен, когда есть некоторая общая обработка в классе, но необходимый подкласс динамически определяется во время выполнения. Иными словами, когда клиент не знает, какой именно подкласс ему может понадобиться.</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/factory-method">Java</a> и Python.</p><h2>Абстрактная фабрика (Abstract Factory)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%90%D0%B1%D1%81%D1%82%D1%80%D0%B0%D0%BA%D1%82%D0%BD%D0%B0%D1%8F_%D1%84%D0%B0%D0%B1%D1%80%D0%B8%D0%BA%D0%B0_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Абстрактная фабрика</b> — порождающий шаблон проектирования, предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, неспецифицируя ихконкретных классов. Шаблон реализуется созданием абстрактного класса Factory, который представляет собой интерфейс для создания компонентов системы (например, для оконного интерфейса онможет создавать окна икнопки). Затем пишутся классы, реализующие этот интерфейс.</blockquote><p>Пример из жизни: Расширим наш пример про двери из простой фабрики. В зависимости от ваших нужд вам понадобится деревянная дверь из одного магазина, железная дверь — из другого или пластиковая — из третьего. Кроме того, вам понадобится соответствующий специалист: столяр для деревянной двери, сварщик для железной двери и так далее. Как вы можете заметить, тут есть зависимость между дверьми.</p><p>Простыми словами: Фабрика фабрик. Фабрика, которая группирует индивидуальные, но связанные/зависимые фабрики без указания их конкретных классов.</p><p>Обратимся к коду. Используем пример про двери. Сначала у нас есть интерфейс Door и несколько его реализаций:</p><p>Затем у нас есть несколько DoorFittingExpert для каждого типа дверей:</p><p>Теперь у нас есть DoorFactory, которая позволит нам создать семейство связанных объектов. То есть фабрика деревянных дверей предоставит нам деревянную дверь и эксперта по деревянным дверям. Аналогично для железных дверей:</p><p>Пример использования:</p><p>Как вы можете заметить, фабрика деревянных дверей инкапсулирует столяра и деревянную дверь, а фабрика железных дверей инкапсулирует железную дверь и сварщика. Это позволило нам убедиться, что для каждой двери мы получим нужного нам эксперта.</p><p>Когда использовать: Когда есть взаимосвязанные зависимости с не очень простой логикой создания.</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/abstract-factory">Java</a> и Python.</p><h2>Строитель (Builder)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D0%BE%D0%B8%D1%82%D0%B5%D0%BB%D1%8C_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Строитель</b> — порождающий шаблон проектирования, который предоставляет способ создания составного объекта. Предназначен для решения проблемы антипаттерна «Телескопический конструктор».</blockquote><p>Пример из жизни: Представьте, что вы пришли в McDonalds и заказали конкретный продукт, например, БигМак, и вам готовят его без лишних вопросов. Это пример простой фабрики. Но есть случаи, когда логика создания может включать в себя больше шагов. Например, вы хотите индивидуальный сэндвич в Subway: у вас есть несколько вариантов того, как он будет сделан. Какой хлеб вы хотите? Какие соусы использовать? Какой сыр? В таких случаях на помощь приходит шаблон «Строитель».</p><p>Простыми словами: Шаблон позволяет вам создавать различные виды объекта, избегая засорения конструктора. Он полезен, когда может быть несколько видов объекта или когда необходимо множество шагов, связанных с его созданием.</p><p>Давайте я покажу на примере, что такое «Телескопический конструктор». Когда-то мы все видели конструктор вроде такого:</p><p>Как вы можете заметить, количество параметров конструктора может резко увеличиться, и станет сложно понимать расположение параметров. Кроме того, этот список параметров будет продолжать расти, если вы захотите добавить новые варианты. Это и есть «Телескопический конструктор».</p><p>Перейдем к примеру в коде. Адекватной альтернативой будет использование шаблона «Строитель». Сначала у нас есть Burger, который мы хотим создать:</p><p>Затем мы берём «Строителя»:</p><p>Пример использования:</p><p>Когда использовать: Когда может быть несколько видов объекта и надо избежать «телескопического конструктора». Главное отличие от «фабрики» — это то, что она используется, когда создание занимает один шаг, а «строитель» применяется при множестве шагов.</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/builder">Java</a> и Python.</p><h2>Прототип (Prototype)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D1%82%D0%BE%D1%82%D0%B8%D0%BF_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote>Задаёт виды создаваемых объектов спомощью экземпляра-прототипа исоздаёт новые объекты путём копирования этого прототипа. Онпозволяет уйти отреализации ипозволяет следовать принципу «программирование через интерфейсы». Вкачестве возвращающего типа указывается интерфейс/ абстрактный класс навершине иерархии, аклассы-наследники могут подставить туда наследника, реализующего этот тип.</blockquote><p>Пример из жизни: Помните Долли? Овечка, которая была клонирована. Не будем углубляться, главное — это то, что здесь все вращается вокруг клонирования.</p><p>Простыми словами: Прототип создает объект, основанный на существующем объекте при помощи клонирования.</p><p>То есть он позволяет вам создавать копию существующего объекта и модернизировать его согласно вашим нуждам, вместо того, чтобы создавать объект заново.</p><p>Обратимся к коду. В PHP это может быть легко реализовано с использованием clone:</p><p>Затем он может быть клонирован следующим образом:</p><p>Также вы можете использовать волшебный метод __clone для изменения клонирующего поведения.</p><p>Когда использовать: Когда необходим объект, похожий на существующий объект, либо когда создание будет дороже клонирования.</p><p>Примеры на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/prototype">Java</a> и Python.</p><h2>Одиночка (Singleton)</h2><p>Википедия <a href="https://ru.wikipedia.org/wiki/%D0%9E%D0%B4%D0%B8%D0%BD%D0%BE%D1%87%D0%BA%D0%B0_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">гласит</a>:</p><blockquote><b>Одиночка</b> — порождающий шаблон проектирования, гарантирующий, что воднопроцессном приложении будет единственный экземпляр некоторого класса, ипредоставляющий глобальную точку доступа кэтому экземпляру.</blockquote><p>Пример из жизни: В стране одновременно может быть только один президент. Один и тот же президент должен действовать, когда того требуют обстоятельства. Президент здесь является одиночкой.</p><p>Простыми словами: Обеспечивает тот факт, что создаваемый объект является единственным объектом своего класса.</p><p>Вообще шаблон одиночка признан антипаттерном, необходимо избегать его чрезмерного использования. Он необязательно плох и может иметь полезные применения, но использовать его надо с осторожностью, потому что он вводит глобальное состояние в ваше приложение и его изменение в одном месте может повлиять на другие части приложения, что вызовет трудности при отладке. Другой минус — это то, что он делает ваш код связанным.</p><p>Прим. перев. Подробнее о подводных камнях шаблона одиночка читайте в <a href="https://tproger.ru/translations/singleton-pitfalls/">нашей статье</a>.</p><p>Перейдем к коду. Чтобы создать одиночку, сделайте конструктор приватным, отключите клонирование и расширение и создайте статическую переменную для хранения экземпляра:</p><p>Пример использования:</p><p>Пример на <a href="https://github.com/iluwatar/java-design-patterns/tree/master/singleton">Java</a>.</p><h2>Часто задаваемые вопросы</h2><p><b>Чем Simple Factory отличается от Factory Method?</b><br />Simple Factory — это обычный класс с методом создания объектов, он не использует наследование. Factory Method определяет интерфейс создания в родительском классе и делегирует выбор конкретного класса наследникам. Simple Factory — одна точка создания, Factory Method — распределённая через полиморфизм.</p><p><b>Когда выбрать Builder вместо Factory?</b><br />Используйте Builder, когда создание объекта требует нескольких шагов или конфигурационных параметров (телескопический конструктор). Factory подходит, когда создание занимает один шаг, а выбор конкретного класса зависит от входных данных.</p><p><b>Почему Singleton считается антипаттерном?</b><br />Singleton вводит глобальное состояние, затрудняет юнит-тестирование (сложно подменить зависимость), создаёт неявные связи между компонентами и нарушает принцип единственной ответственности. Используйте его только когда действительно нужен единственный экземпляр — например, для пула соединений или конфигурации.</p><p><b>Можно ли комбинировать порождающие шаблоны?</b><br />Да, это распространённая практика. Например, Abstract Factory может использовать Factory Method для создания конкретных продуктов, а Builder может применять Prototype для клонирования шаблонного объекта вместо создания с нуля.</p>]]></content:encoded>
    </item>
    <item>
      <title>Курс «Шаблоны проектирования»</title>
      <link>https://tproger.ru/video/design-patterns</link>
      <comments>https://tproger.ru/video/design-patterns?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Лапа]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/video/design-patterns</guid>
      <description><![CDATA[<p>Курс объясняет понятие шаблонов проектирования и разбирает более десятка распространённых решений для программирования на Java.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/video/design-patterns">Курс «Шаблоны проектирования»</a>»</p>]]></description>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Обучающие курсы]]></category>
      <category><![CDATA[Видео]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 03 May 2017 18:42:11 GMT</pubDate>
      <content:encoded><![CDATA[<p>Созданный в 2015 году курс за авторством Романа Бровко, посвященный шаблонам проектирования — проверенным и готовым к использованию решениям часто возникающих в повседневном программировании задач. Курс охватывает собственно понятие паттерна, а также более десятка самых распространенных в использовании шаблонов. Все примеры реализованы на Java, но подойдут для понимания и программистам на других языках.</p><p>Также прочитайте <a href="https://tproger.ru/translations/design-patterns-for-beginners/">наш материал</a>, в котором мы популярно рассказываем о том, что такое шаблоны проектирования, зачем они нужны и как их использовать.</p>]]></content:encoded>
    </item>
    <item>
      <title>Джедайские приемы на JavaScript: магические свойства транслятора событий</title>
      <link>https://tproger.ru/translations/event-emitter-javascript</link>
      <comments>https://tproger.ru/translations/event-emitter-javascript?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Лапа]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/event-emitter-javascript</guid>
      <description><![CDATA[<p>Шаблон «транслятор событий» даёт участку асинхронного кода возможность сообщить о выполненной задаче, а другим частям — подписаться и услышать сигнал.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/event-emitter-javascript">Джедайские приемы на JavaScript: магические свойства транслятора событий</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 05 Nov 2016 14:16:59 GMT</pubDate>
      <content:encoded><![CDATA[<h3>О чем мы?</h3><p>Event Emitter можно перевести как “транслятор” или “эмиттер” событий. Звучит как название штуки, умеющей генерировать событие, которое может “услышать” кто угодно.</p><p>Представьте себе такую схему: в вашем асинхронном коде определенный участок может “крикнуть” остальным, что он выполнил свою задачу, а другие части “услышат” этот сигнал и примут соответствующие меры.</p><p>Event Emitter — это шаблон, который можно реализовать разными способами. Основная идея в том, чтобы грамотно создать основу для управления событиями и реализовать возможность любым элементам “подписаться” на него (и быть в курсе происходящего). С другими шаблонами проектирования вы можете познакомиться в <a href="https://tproger.ru/translations/design-patterns-for-beginners/">нашей статье</a>.</p><p>Уже интересно, как такая магия может работать? Итак, мы хотим добиться кода, который затем можно будет использовать так:</p><p>Начнем.</p><h3>Реализация</h3><p>Как видите, конструктор нашего класса будет инициализировать поле events, пока что делая его пустым объектом. Задача этого поля — хранить события, “подписавшиеся” на нас (то есть в нем будут храниться функции).</p><p>Метод subscribe:</p><p>Этот метод принимает в качестве аргументов название события (например, event:name-changed, как в нашем примере) и функцию, которая будет вызываться, когда будет инициироваться транслируемое событие.</p><p>Одна из ключевых особенностей функций в JavaScript состоит в том, что функции — это этакие “объекты первого класса”, то есть мы можем передать функцию в качестве параметра другой функции, как в методе subscribe().</p><p>Метод emit:</p><p>Этот метод принимает имя события, которое мы хотим всем транслировать, и данные, которые будут отправляться в момент этого события. Если в экземпляре класса сохранены какие-то подписанные на него события, мы проходимся по каждому из них и вызываем каждое, передавая ему данные, которые хотим транслировать.</p><p>Собственно, с реализацией паттерна мы закончили. Но пока остается одна проблема: нам нужно будет “отписать” функции, которые нам больше не нужны. Не сделаем этого — столкнемся с утечкой памяти.</p><p>Давайте решим эту проблему. Пусть метод subscribe() возвращает функцию unsubscribe(), которую позже можно будет использовать, чтобы отписаться от события.</p><p>Как мы помним, в JavaScript особенные функции — их легко можно вернуть из другой функции. Теперь метод subscribe() можно использовать следующим образом:</p><p>Вызывая сохраненную в переменной функцию unsubscribe(), мы отписываемся от события.</p><p>Вот и все. Пока-пока, утечки памяти!</p><p>Вот <a href="https://plnkr.co/edit/TEM1eA9ahavEybuEKn6E?p=preview">тут</a> можно попробовать весь получившийся код в действии.</p>]]></content:encoded>
    </item>
    <item>
      <title>Подводные камни Singleton: почему самый известный шаблон проектирования нужно использовать с осторожностью</title>
      <link>https://tproger.ru/translations/singleton-pitfalls</link>
      <comments>https://tproger.ru/translations/singleton-pitfalls?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/singleton-pitfalls</guid>
      <description><![CDATA[<p>Паттерн «Одиночка» гарантирует единственный объект класса с глобальным доступом, но за этой простотой скрывается ряд серьёзных недостатков.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/singleton-pitfalls">Подводные камни Singleton: почему самый известный шаблон проектирования нужно использовать с осторожностью</a>»</p>]]></description>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 17 Sep 2016 20:33:28 GMT</pubDate>
      <content:encoded><![CDATA[<p>Паттерн <a href="https://tproger.ru/translations/design-patterns-for-beginners/#singleton">“Одиночка”</a> — пожалуй, самый известный паттерн проектирования. Тем не менее, он не лишен недостатков, поэтому некоторые программисты (например, <a href="http://www.yegor256.com/2016/06/27/singletons-must-die.html">Егор Бугаенко</a>) считают его антипаттерном. Разбираемся в том, какие же подводные камни таятся в Singleton’е.</p><h3>Определение паттерна</h3><p>Само описание паттерна достаточно простое — класс должен гарантированно иметь лишь один объект, и к этому объекту должен быть предоставлен глобальный доступ. Скорее всего, причина его популярности как раз и кроется в этой простоте — всего лишь один класс, ничего сложного. Это, наверное, самый простой для изучения и реализации паттерн. Если вы встретите человека, который только что узнал о существовании паттернов проектирования, можете быть уверены, что он уже знает про Singleton. Проблема заключается в том, что когда из инструментов у вас есть только молоток, всё вокруг выглядит как гвозди. Из-за этого “Одиночкой” часто злоупотребляют.</p><h3>Простейшая реализация</h3><p>Как уже говорилось выше, в этом нет ничего сложного:</p><ul><li>Сделайте конструктор класса приватным, чтобы не было возможности создать экземпляр класса извне.</li><li>Храните экземпляр класса в private static поле.</li><li>Предоставьте метод, который будет давать доступ к этому объекту.</li></ul><h3>Принцип единственной обязанности</h3><p>В объектно-ориентированном программировании существует правило хорошего тона — <a href="https://ru.wikipedia.org/wiki/Принцип_единственной_обязанности">“Принцип едиственной обязанности”</a> (Single Responsibility Principle, первая буква в аббревиатуре <a href="https://ru.wikipedia.org/wiki/SOLID_(объектно-ориентированное_программирование)">SOLID</a>). Согласно этому правилу, каждый класс должен отвечать лишь за один какой-то аспект. Совершенно очевидно, что любой Singleton-класс отвечает сразу за две вещи: за то, что класс имеет лишь один объект, и за реализацию того, для чего этот класс вообще был создан.</p><p>Принцип единственной обязанности был создан не просто так — если класс отвечает за несколько действий, то, внося изменения в один аспект поведения класса, можно затронуть и другой, что может сильно усложнить разработку. Так же разработку усложняет тот факт, что переиспользование (reusability) класса практически невозможно. Поэтому хорошим шагом было бы, во-первых, вынести отслеживание того, является ли экземпляр класса единственным, из класса куда-либо во вне, а во-вторых, сделать так, чтобы у класса, в зависимости от контекста, появилась возможность перестать быть Singleton’ом, что позволило бы использовать его в разных ситуациях, в зависимости от необходимости (т.е. с одним экземпляром, с неограниченным количество экземпляров, с ограниченным набором экземпляров и так далее).</p><h3>Тестирование</h3><p>Один из главных минусов паттерна “Одиночка” — он сильно затрудняет юнит-тестирование. “Одиночка” привносит в программу глобальное состояние, поэтому вы не можете просто взять и изолировать классы, которые полагаются на Singleton. Поэтому, если вы хотите протестировать какой-то класс, то вы обязаны вместе с ним тестировать и Singleton, но это ещё полбеды. Состояние “Одиночки” может меняться, что порождает следующие проблемы:</p><ul><li>Порядок тестов теперь имеет значение;</li><li>Тесты могут иметь нежелательные сторонние эффекты, порождённые Singleton’ом;</li><li>Вы не можете запускать несколько тестов параллельно;</li><li>Несколько вызовов одного и того же теста могут приводить к разным результатам.</li></ul><p>На эту тему есть отличный доклад с “Google Tech Talks”:</p><h3>Скрытые зависимости</h3><p>Обычно, если классу нужно что-то для работы, это сразу понятно из его методов и конструкторов. Когда очевидно, какие зависимости есть у класса, гораздо проще их предоставить. Более того, в таком случае вы можете использовать вместо реально необходимых зависимостей заглушки для тестирования. Если же класс использует Singleton, это может быть совершенно не очевидно. Всё становится гораздо хуже, если экземпляру класса для работы необходима определённая инициализация (например, вызов метода init(...) или вроде того). Ещё хуже, если у вас существует несколько Singleton’ов, которые должны быть созданы и инициализированы в определённом порядке.</p><h3>Загрузчик класса</h3><p>Если говорить о Java, то обеспечение существования лишь одного экземпляра класса, которое так необходимо для Singleton, становится всё сложнее. Проблема в том, что классическая реализация не проверяет, существует ли один экземпляр на JVM, он лишь удостоверяется, что существует один экземпляр на classloader. Если вы пишете небольшое клиентское приложение, в котором используется лишь один classloader, то никаких проблем не возникнет. Однако если вы используете несколько загрузчиков класса или ваше приложение должно работать на сервере (где может быть запущено несколько экземпляров приложения в разных загрузчиках классов), то всё становится очень печально.</p><h3>Десериализация</h3><p>Ещё один интересный момент заключается в том, что на самом деле стандартная реализация Singleton не запрещает создавать новые объекты. Она запрещает создавать новые объекты через конструктор. А ведь существуют и другие способы создать экземпляр класса, и один из них — сериализация и десериализация. Полной защиты от намеренного создания второго экземпляра Singleton’а можно добиться только с помощью использования enum’а с единственным состоянием, но это — неоправданное злоупотребление возможностями языка, ведь очевидно, что enum был придуман не для этого.</p><h3>Потоконебезопасность</h3><p>Один из популярных вариантов реализации Singleton содержит ленивую инициализацию. Это значит, что объект класса создаётся не в самом начале, а лишь когда будет получено первое обращение к нему. Добиться этого совсем не сложно:</p><p>Однако здесь начинаются проблемы с потоками, которые могут создавать несколько различных объектов. Происходит это примерно так:</p><ul><li>Первый поток обращается к getInstance(), когда объект ещё не создан;</li><li>В это время второй тоже обращается к этому методу, пока первый ещё не успел создать объект, и сам создаёт его;</li><li>Первый поток создаёт ещё один, второй, экземпляр класса.</li></ul><p>Разумеется, можно просто пометить метод как synchronised, и эта проблема исчезнет. Проблема заключается в том, что, сохраняя время на старте программы, мы теперь будем терять его каждый раз при обращении к Singleton’у из-за того, что метод синхронизирован, а это очень дорого, если к экземпляру приходится часто обращаться. А ведь единственный раз, когда свойство synchronised действительно требуется — первое обращение к методу.</p><p>Есть два способа решить эту проблему. Первый — пометить как synchronised не весь метод, а только блок, где создаётся объект:</p><p>Не забывайте, что это нельзя использовать в версии Java ниже, чем 1.5, потому что там используется иная модель памяти. Также не забудьте пометить поле instance как volatile.</p><p>Второй путь — использовать паттерн “Lazy Initialization Holder”. Это решение основано на том, что вложенные классы не инициализируются до первого их использования (как раз то, что нам нужно):</p><h3>Рефлексия</h3><p>Мы запрещаем создавать несколько экземпляров класса, помечая конструктор приватным. Тем не менее, используя рефлексию, можно без особого труда изменить видимость конструктора с private на public прямо во время исполнения:</p><p>Конечно, если вы используете Singleton только в своём приложении, переживать не о чем. А вот если вы разрабатываете модуль, который затем будет использоваться в сторонних приложениях, то из-за этого могут возникнуть проблемы. Какие именно, зависит от того, что делает ваш “Одиночка” — это могут быть как и риски, связанные с безопасностью, так и просто непредсказуемое поведение модуля.</p><h3>Заключение</h3><p>Несмотря на то, что паттерн Singleton очень известный и популярный, у него есть множество серьёзных недостатков. Чем дальше, тем больше этих недостатков выявляется, и оригинальные паттерны из книги <a href="https://ru.wikipedia.org/wiki/Design_Patterns">GOF “Design Patterns”</a> часто сегодня считаются антипаттернами. Тем не менее, сама идея иметь лишь один объект на класс по-прежнему имеет смысл, но достаточно сложно реализовать ее правильно.</p>]]></content:encoded>
    </item>
    <item>
      <title>Паттерны проектирования для новичков</title>
      <link>https://tproger.ru/translations/design-patterns-for-beginners</link>
      <comments>https://tproger.ru/translations/design-patterns-for-beginners?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Vladimir Gabrinevski]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/design-patterns-for-beginners</guid>
      <description><![CDATA[<p>Что такое шаблоны проектирования, какими они бывают, где и как их используют — подробно объяснили в нашей статье с примерами.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/design-patterns-for-beginners">Паттерны проектирования для новичков</a>»</p>]]></description>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 23 Jul 2015 10:49:28 GMT</pubDate>
      <content:encoded><![CDATA[<p>Внимание! Материал был обновлён и разделён на три части. Предлагаем вам ознакомиться с ним по следующим ссылкам:</p><ul><li><a href="https://tproger.ru/translations/design-patterns-simple-words-1/">Первая часть</a></li><li><a href="https://tproger.ru/translations/design-patterns-simple-words-2/">Вторая часть</a></li><li><a href="https://tproger.ru/translations/design-patterns-simple-words-3/">Третья часть</a></li></ul><p>Если вы когда-либо интересовались, что представляют собой шаблоны проектирования, то добро пожаловать. В этой статье я расскажу, что это такое, зачем они нужны, как их использовать, и приведу примеры наиболее распространенных шаблонов на PHP.</p><h2>Что такое шаблоны проектирования</h2><p>Шаблоны проектирования — это проверенные и готовые к использованию решения часто возникающих в повседневном программировании задач. Это не класс и не библиотека, которую можно подключить к проекту, это нечто большее. Шаблон проектирования, подходящий под задачу, реализуется в каждом конкретном случае. Кроме того, он не зависит от языка программирования. Хороший шаблон легко реализуется в большинстве, если не во всех языках, в зависимости от выразительных средств языка. Следует, однако, помнить, что такой шаблон, будучи примененным неправильно или к неподходящей задаче, может принести немало проблем. Тем не менее, правильно примененный шаблон поможет решить задачу легко и просто.</p><p>Существует три типа шаблонов:</p><ul><li>структурные;</li><li>порождающие;</li><li>поведенческие.</li></ul><p><b>Структурные</b> шаблоны определяют отношения между классами и объектами, позволяя им работать совместно.</p><p><b>Порождающие</b> шаблоны предоставляют механизмы инициализации, позволяя создавать объекты удобным способом.</p><p><b>Поведенческие</b> шаблоны используются для того, чтобы упростить взаимодействие между сущностями.</p><h2>Зачем нужны шаблоны проектирования</h2><p>Шаблон проектирования, по своей сути, это продуманное решение той или иной задачи. Если вы столкнулись с известной задачей, почему бы не использовать готовое решение, проверенное опытом?</p><p>Пример</p><p>Давайте представим, что вам необходимо объединить два класса, которые выполняют различные операции в зависимости от ситуации. Эти классы интенсивно используются существующей системой, что не позволяет удалить один из них и добавить его функциональность во второй. Кроме того, изменение кода потребует его тщательного тестирования, поскольку такой рефакторинг ведет к неизбежным ошибкам. Вместо этого вы можете реализовать шаблоны «Стратегия» и «Адаптер» и с их помощью решить задачу.</p><p>Просто, не правда ли? Давайте посмотрим поближе на шаблон «Стратегия».</p><h3>Паттерн проектирования «Стратегия»</h3><blockquote><b>Стратегия</b> — поведенческий шаблон, который позволяет выбрать поведение программы в процессе выполнения в зависимости от контекста путем инкапсуляции нескольких алгоритмов в разных классах.</blockquote><p>В примере выше выбор стратегии основан на значении переменной $context, которое было в момент создания объекта. Если значение было "context_for_class_one", программа будет использовать класс class_one. И наоборот.</p><h2>Где это можно использовать</h2><p>Представьте, что вы разрабатываете класс, который может создать или обновить запись в базе данных. В обоих случаях входные параметры будут одни и те же (имя, адрес, номер телефона и т. п.), но, в зависимости от ситуации, он будет должен использовать различные функции для обновления и создания записи. Можно каждый раз переписывать условие if/else, а можно создать один метод, который будет принимать контекст:</p><p>Обычно шаблон «Стратегия» подразумевает инкапсуляцию алгоритмов в классы, но в данном случае это излишне. Помните, что вы не обязаны следовать шаблону слово в слово. Любые варианты допустимы, если они решают задачу и соответствуют концепции.</p><h3>Шаблон «Адаптер»</h3><blockquote><b>Адаптер</b> — структурный шаблон, который позволяет использовать класс, реализующий нужные функции, но имеющий неподходящий интерфейс.</blockquote><p>Также он позволяет изменить некоторые входные данные для совместимости с интерфейсом внутреннего класса.</p><h4>Как его использовать?</h4><p>Другое название адаптера — «Обертка». Он «оборачивает» новый интерфейс вокруг класса для его использования. Классический пример: вам надо создать класс предметной модели, имея классы объектов в базе данных. Вместо того, чтобы обращаться к табличным классам напрямую и вызывать их методы по одному, вы можете инкапсулировать вызовы этих методов в одном методе в адаптере. Это не только позволит повторно использовать набор операций, но и избавит вас от постоянного переписывания большого количества кода, если вам потребуется выполнить тот же набор действий в другом месте.</p><p>Сравните два примера.</p><p>Без адаптера:</p><p>Если нам придется использовать такой код повторно, мы будем вынуждены переписывать все это заново.</p><p>С использованием адаптера:</p><p>Мы можем создать класс-обертку Account:</p><p>Теперь мы можем использовать класс Account каждый раз и, кроме того, мы можем добавить в него дополнительные функции.</p><h3>Шаблон «Метод-фабрика»</h3><blockquote><b>Фабрика</b> — порождающий шаблон, который представляет собой класс с методом для создания различных объектов.</blockquote><p>Основная цель этого шаблона — инкапсулировать процедуру создания различных классов в одной функции, которая в зависимости от переданного ей контекста возвращает необходимый объект.</p><h4>Как его использовать?</h4><p>Фабрика обычно используется для создания различных вариантов базового класса. Допустим, у вас есть класс кнопки — Button — и три варианта — ImageButton, InputButton и FlashButton. С помощью фабрики вы можете создавать различные варианты кнопок в зависимости от ситуации.</p><p>Сначала создадим три класса:</p><p>Теперь мы можем написать нашу фабрику:</p><p>и использовать ее:</p><p>На выходе должен получиться HTML со всеми типами кнопок. Таким образом мы получили возможность указать, кнопку какого типа мы хотим получить, и использовать код повторно.</p><h3>Шаблон «Декоратор»</h3><blockquote><b>Декоратор</b> — это структурный шаблон, который позволяет добавить новое поведение объекту в процессе выполнения программы в зависимости от ситуации.</blockquote><p>Цель — в расширении поведения конкретного объекта без необходимости изменять поведение базового класса. Это позволит использовать несколько декораторов одновременно. Этот шаблон — альтернатива наследованию. В отличие от наследования, декоратор добавляет поведение в процессе выполнения программы.</p><p>Для реализации декоратора нам понадобится:</p><ol><li>Унаследовать класс-декоратор от базового.</li><li>Добавить поле со ссылкой на базовый класс в декоратор.</li><li>Передать ссылку на декорируемый объект в конструктор декоратора.</li><li>Перенаправить методы из декоратора на декорируемый объект.</li><li>Переопределить методы в декораторе, поведение которых необходимо изменить.</li></ol><h4>Как его использовать?</h4><p>Предположим, что у нас есть объект, который должен иметь определенное поведение в определенной ситуации. Например, у нас есть HTML-ссылка для выхода из аккаунта, которая должна по-разному показываться в зависимости от того, на какой странице мы находимся. Это тот самый случай, когда нам помогут декораторы.</p><p>Сначала определимся, какие «декорации» нам нужны:</p><ul><li>Если мы на заглавной странице и вошли в аккаунт, ссылка должна быть в h2-теге.</li><li>Если мы на любой другой странице и вошли в аккаунт, ссылка должна быть подчеркнутой.</li><li>Если мы вошли в аккаунт, ссылка должна быть в strong-теге.</li></ul><p>Теперь мы можем написать сами декораторы:</p><p>Теперь мы можем использовать их так:</p><p>Обратите внимание, как можно использовать несколько декораторов на одном объекте. Все они используют функцию __call для вызова оригинального метода. Если мы войдем в аккаунт и перейдем на заглавную страницу, результат будет такой:</p><h3>Шаблон «Одиночка»</h3><blockquote><b>Одиночка</b> — порождающий шаблон, который позволяет убедиться, что в процессе выполнения программы создается только один экземпляр класса с глобальным доступом.</blockquote><p>Его можно использовать как точку «координации» для других объектов, поскольку поля «Одиночки» будут одинаковы для всех, кто его вызывает.</p><h4>Как его использовать?</h4><p>Если вам необходимо передавать определенный экземпляр из класса в класс, вы можете передавать его каждый раз через конструктор или использовать «Одиночку». Допустим, у вас есть класс Session, который содержит данные о текущей сессии. Поскольку сессия инициализируется только один раз, мы можем реализовать его так:</p><p>Теперь мы можем получить доступ к сессии из различных участков кода, даже из других классов. Метод getInstance всегда будет возвращать одну и ту же сессию.</p><h2>Заключение</h2><p>В этой статье мы рассмотрели только наиболее часто встречающиеся шаблоны из множества. Если вы хотите узнать больше о шаблонах проектирования, вы найдете достаточно информации на <a href="https://ru.wikipedia.org/wiki/%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F">Википедии</a>. Для более полной информации обратите внимание на знаменитую книгу «Приемы объектно-ориентированного проектирования» «Банды четырех».</p><p>И последнее: при использовании того или иного шаблона убедитесь, что вы решаете задачу правильным способом. Как уже упоминалось, при неправильном использовании шаблоны проектирования могут доставить больше проблем, чем решить. Но при правильном — их пользу нельзя переоценить.</p>]]></content:encoded>
    </item>
  </channel>
</rss>