- Что такое паттерн (шаблон) проектирования.
- Когда используют шаблоны.
- Какими бывают паттерны проектирования.
- Как выбрать шаблон?
- Выводы.
Начинающие программисты всегда приходят к точке, когда их код превращается в "спагетти". Его трудно читать, он содержит массу повторений, лишних функций, а добавление нового функционала превращается в десятый круг ада.
Одно из лучших средств предотвращения этого – использовать паттерны проектирования (Design Patterns). Является ли это серебряной пулей, какие преимущества и недостатки паттернов существуют, и какие из них необходимо знать разработчикам? Ответы разбираем ниже.
Что такое паттерн (шаблон) проектирования?
Паттерны – это типичные архитектурные решения проблем, которые часто встречаются при разработке ПО. Их другое название – шаблоны, и что интересно – человечество очень часто окружает себя шаблонами в повседневной жизни:
- одинаковые гнезда розетки и формы вилок в помещениях – универсальное решение для электропитания;
- вилки и ложки – инструменты потребления почти любой пищи;
- чашки – емкости для размещения любой жидкости и так далее.
Человек всегда стремится упростить традиционную деятельность, и это не могло обойти стороной программирование.
Идеи создания универсальных правил для качественной разработки существовали ещё до 90-х годов прошлого века, но действительно прорывной стала работа "Design Patterns: Elements of Reusable Object-Oriented Software" (1994) авторства Эриха Гамма, Ричарда Хелма, Ральфа Джонсона и Джона Влиссидеса, которые именуют себя как "Банда четырех" (Gang of Four, GoF).
В книге описано 23 паттерна и их применение в объектно-ориентированном дизайне. Этот труд стал фундаментальным и теперь паттерны gof составляют костяк многих обсуждений качественного кода.
Когда используют паттерны
В разработке шаблоны используют при необходимости приведения кода к следующим критериям:
- Читабельность – другие разработчики должны без труда понимать написанное.
- Масштабируемость – легкость в создании нового функционала.
- Поддерживаемость – обновление кодовой базы должно проходить как можно более плавно.
Также они способны повысить скорость и производительность разработчика – паттерны это действительно обеспечивают. Они хорошо справляются и со следующими задачами:
- уменьшение количества потенциальных ошибок и узких мест;
- упрощение рефакторинга;
- уменьшение технического долга;
- улучшение коммуникации девелоперов с другими программистами, проектными менеджерами, владельцами и т. д.
Необходимость использовать шаблоны проектирования растет вместе с увеличением кодовой базы, особенно при коммерческой разработке – когда создаваемое ПО должно приносить прибыль.
Важно помнить, что использование паттернов иногда вовсе неуместно. Оно может значительно усложнить читабельность, громоздкость и масштабируемость кода. Например, несложный функционал, который нечасто используется и занимает немного места в коде, не требует pattern-вмешательства. А вот репетативный код, решающий классические задачи (сортировка, перебор данных и т. д.) – идеальный претендент на применение шаблона.
Чтобы не ошибиться сначала выясните контекст вашей проблемы, а уже потом выбирайте паттерны программирования, которые лучше всего удовлетворяют требованиям.
Какими бывают паттерны проектирования
В своей книге GoF выделяют три больших семейства:
Семейство |
Краткое описание |
Порождающие паттерны или Creational Patterns |
Предоставляют лучшие способы Популярные примеры: "Абстрактная фабрика" (Abstract Factory), "Одиночка" (Singleton), "Прототип" (Prototype), "Фабричный метод" (Factory Method). |
Структурные паттерны или Structural Patterns |
Фокусируются на композиции объекта. Помогают убедиться в том, что изменение части системы не повлечет за собой необходимость изменений в других ее составляющих. Популярные примеры: "Прокси" (Proxy), "Адаптер" (Adapter), "Компоновщик" (Composite), "Фасад" (Facade). |
Паттерны поведения или Behavioral Patterns |
Зона ответственности – алгоритмы и обмен информацией между объектами. Популярные примеры: "Посетитель" (Visitor), "Итератор" (Iterator), "Цепочка обязанностей" (Chain of Responsibility), "Стратегия" (Strategy). |
Рассмотрим более подробно некоторые из них.
Порождающие
Порождающие паттерны – это надежные помощники в создании объектов таким образом, чтобы в будущем с ними было максимально легко работать.
Дадим краткое описание некоторых шаблонов:
- Паттерн Одиночка / Синглтон обеспечивает наличие только одного экземпляра класса с глобальной точкой доступа. Singleton распространен в задачах конфигураций или логирования в приложениях, где нужен единый контролируемый доступ.
- Шаблон Прототип позволяет создавать новые объекты путем копирования существующих экземпляров. Используется Prototype в ситуациях, когда создание объекта слишком дорого, например, при клонировании сложных или ресурсоемких объектов.
- Фабричный метод определяет интерфейс для создания объектов, но позволяет подклассам самостоятельно определять тип создаваемых объектов. Fabric Method полезен во многофункциональных приложениях, где классы должны иметь возможность выбирать тип объектов, например, при работе с различными форматами документов, системами онлайн платежей и т. д.
- Абстрактная фабрика определяет интерфейс для создания семейств связанных объектов без указания их конкретных классов. Используют Abstract Factory для создания различных компонентов пользовательского интерфейса, которые должны работать вместе и обеспечивать единый стиль (светлая/темная тема веб-сайта и т. д.).
Рассмотрим пример на паттерне Singleton. Представьте себе простое приложение – музыкальный плеер. Он позволяет пользователям воспроизводить музыкальные файлы. Однако одновременно должен работать только один экземпляр плеера – возможность открытия нескольких одновременно надо пресечь. Этого можно достичь с помощью шаблона Singleton.
Простой пример кода на языке C#:
public class MusicPlayer
{
private static MusicPlayer _instance;
private MusicPlayer()
{
// Инициализируем музыкальный плеер (например, загружаем плейлисты)
}
public static MusicPlayer Instance
{
get
{
if (_instance == null)
{
_instance = new MusicPlayer();
}
return _instance;
}
}
public void PlaySong(string songPath)
{
// Запустить песню
}
public void PauseSong()
{
// Поставить на паузу
}
public void StopSong()
{
// Остановить воспроизведение
}
}// Получаем экземпляр MusicPlayer
MusicPlayer player = MusicPlayer.Instance;// Используем функционал MusicPlayer
player.PlaySong("C:\\Users\\yourUsername\\Music\\mySong.mp3");
player.PauseSong();
player.StopSong();
Каждый раз, когда в разных участках проекта вам нужно будет создавать экземпляр плеера для соответствующего взаимодействия, вы всегда будете работать только с одним и тем же экземпляром, избегая дублирования.
Если вы программируете на языке си шарп, подробно разобрать популярные паттерны проектирования C# с примерами вы можете по ссылке.
Структурные
Из краткого описания в таблице легко сделать вывод, что структурные паттерны позволяют сформировать надежную, масштабируемую и поддерживаемую архитектуру проекта. Краткое знакомство:
- Прокси обеспечивает объект-посредник для контроля доступа к другому объекту. Обычно шаблон Proxy используют для реализации "ленивой" загрузки, когда объект создается или инициализируется только при обращении к нему (например, загрузка картинок с высоким разрешением).
- Адаптер позволяет объектам с несовместимыми интерфейсами работать вместе. Применяется паттерн Adapter для интеграции новых компонентов в существующую систему без изменения ее кода. Подходит для использования новой библиотеки в старом приложении.
- Компоновщик используется для иерархической компоновки объектов для дальнейшей работы с ними как с единым объектом. Используется для создания древовидных структур, таких как файловые системы или GUI, где каждый узел может быть как простым, так и Composite объектом.
- Фасад (Facade) предоставляет упрощенный интерфейс для взаимодействия со сложной системой или набором классов. Он уменьшает сложность работы с подсистемами и предоставляет пользователям единый входной интерфейс для выполнения рутинных операций.
Изучить именно структурные паттерны проектирования C# (с примерами) вы можете по ссылке.
Поведенческие
Паттерны поведения в первую очередь определяют связи между объектами и то, как они осуществляют обмен информацией. Например:
- Паттерн Посетитель (Visitor) позволяет добавлять новые операции к объектам без изменения их оригинальных классов. Используется для взаимодействия с объектами со сложной структурой, когда внесение дополнительной логики в оригинальные классы неоправданно усложняет код.
- Итератор / Iterator предоставляет удобный механизм последовательного и простого доступа к элементам коллекции, несмотря на сложность ее построения. Данный паттерн поведения популярен при обходе элементов контейнеров, например списков или массивов – он предоставляет универсальный интерфейс для различных типов коллекций.
- Цепочка обязанностей или же паттерн Chain of Responsibility позволяет передавать запрос цепочкой обработчиков, пока один из них не обработает запрос. Незаменим при обработке запросов на сервере, где каждый обработчик может передать запрос следующему обработчику в цепочке: проверка при авторизации на сайте, обработка событий в GUI и тому подобное.
Для входа в паттерны проектирования книга от Gang of Four будет хорошей точкой отсчета. Вы познакомитесь с классикой и академическим раскрытием темы, используя паттерны gof. Если же вы хотите обогатить свои знания шаблонов, но предпочитаете язык Java, рекомендуем видео курс "Паттерны проектирования Java".
Как выбрать паттерн?
Сначала вы должны проанализировать задачу – для большей понятности выполните ее декомпозицию, разбив на несколько составляющих. При этом используйте системный подход: просчитайте, как ваше решение повлияет на весь проект, какие элементы оно затронет сейчас, и какое влияние оно окажет на добавление нового кода.
Если вы уже работаете в IT-компании, ваши коллеги, тимлид или архитектор могут подсказать вам целесообразность использования того или иного паттерна, раскрыть нюансы уже существующей архитектуры, кодового стиля и многое другое.
Только после тщательного анализа можно переходить к подбору шаблона, учитывая все преимущества и недостатки. Кстати, в этих задачах хорошими помощниками будут бесплатные AI-ассистенты вроде ChatGPT, Gemini и др.
Также не забывайте об использовании других методик улучшения кодовой читабельности, масштабирования и чистоты:
- SOLID принципы – они регламентируют 5 основных принципов создания структурированного, качественного кода. Недавно мы проводили вебинар, на котором разбирали каждый принцип в деталях, приглашаем к просмотру! А если вас интересует прикладной характер SOLID принципов на Java, можете пройти данный видео курс.
- GRASP (General Responsibility Assignment Software Patterns) – паттерны для объектно-ориентированного проектирования. Они не имеют выраженной структуры и носят более абстрактный характер, чем паттерны gof.
- DRY (Don't Repeat Yourself) – главная идея данного принципа заключается в создании кода, который не будет иметь дубликаций в проекте.
- KISS (Keep It Simple, Stupid) – регламентирует написание как можно более простого кода, чтобы его можно было легко читать и понимать.
- Рефакторинг – возвращение к уже написанному коду с целью его улучшения без изменения функциональности.
- Другие техники, зависящие от проектов.
Бесплатные вебинары по схожей тематике:
Выводы
Паттерны играют ключевую роль в современной разработке. Они аккумулируют в себе лучшие практики создания кодовой базы таким образом, чтобы достичь максимальной легкости и эффективности разработки, особенно на больших проектах.
Конечно, не всегда их использование уместно – нужно анализировать задачи и продумывать последствия применения того или иного шаблона, чтобы не получить огромный чемодан без ручки.
Развивайте вашу экспертизу в области паттернов – это win-win стратегия. С одной стороны перед работодателями вы предстанете как опытный и высококвалифицированный специалист, а с другой – ваши программные решения будут иметь элегантный характер, легкость в чтении, поддержке и масштабировании.
Используете ли вы паттерны во время разработки? Возможно, только изучаете? Оставляйте в комментариях ваши ответы!
Статьи по схожей тематике