Структурные паттерны проектирования: для каких задач нужны, виды и примеры реализации
В этой части материала про паттерны разбираемся, что такое структурные паттерны проектирования и какие задачи они решают, а также изучаем три самых часто используемых.
Еще раз про паттерны
Паттерны проектирования — это решения распространенных проблем при разработке приложений. Также они известны как шаблоны проектирования, паттерны объектно-ориентированного программирования и design patterns. В отличие от готовых функций или библиотек, паттерн представляет собой не конкретный код, а общую концепцию решения проблемы, которую еще нужно подстроить под задачи.
Всего существует 23 классических паттерна, которые были описаны в книге «Банды четырех». В зависимости от того, какие задачи они решают, делятся на порождающие, структурные и поведенческие.
Структурные паттерны
Согласно Википедии, структурные шаблоны (structural patterns) — шаблоны проектирования, в которых рассматривается вопрос о том, как из классов и объектов образуются более крупные структуры.
Проще говоря, структурные паттерны связаны с композицией объектов или тем, как сущности могут использовать друг друга. К ним относятся:
- Facade, или Фасад
- Adapter, или Адаптер
- Decorator, или Декоратор
- Bridge, или Мост
- Composite, или Компоновщик
- Front controller, или Единая точка входа
- Flyweight, или Приспособленец, или Лекговес
- Proxy, или Заместитель
3 самых популярных структурных паттерна
По мнению разработчиков MediaSoft Facade, Adaptor и Decorator — это самые используемые структурные паттерны в разработке. Давайте разберемся, с какими задачами они помогают справляться, и посмотрим на примеры их реализации.
Facade (Фасад)
Согласно Википедии, Facade — структурный шаблон проектирования, позволяющий скрыть сложность системы путём сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы.
Проще говоря: Facade предоставляет упрощенный интерфейс для сложной системы.
Еще проще: При оплате покупки через Apple Pay вы подносите телефон к устройству и оплачиваете покупку. Кажется, что все просто. Но на самом деле внутри этого процесса происходит гораздо больше вещей. Этот упрощенный интерфейс называется фасадом.
Когда нужен: Используется в библиотеках и позволяет описать их так, чтобы пользователю не нужно было вникать в их реализацию.
Структура паттерна:
- Фасад — интерфейс для легкого доступа. Предоставляет только тот функционал, который нужен клиенту, и скрывает всё остальное.
- Сложная система из классов и методов.
Как создать:
- Создаем интерфейс фасада. В нем перечисляем нужные нам методы системы, которые предоставляет фасад.
- Создаем класс фасада, который реализует этот интерфейс.
- В методах этого класса реализуем обращения к сложной системе, которая скрывается за фасадом.
Пример реализации:
class CPU < public void execute() < . >> class Memory < public void load(long position, byte[] data) < . >> class HardDrive < public byte[] read(long lba, int size) < . >> class Computer < // Facade private CPU cpu = new CPU(); private Memory memory = new Memory(); private HardDrive hardDrive = new HardDrive(); public void startComputer() < memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE)); cpu.execute(); >> class Application < // Client public static void main(String[] args) < Computer computer = new Computer(); computer.startComputer(); >>
// Создаем классы, Doors и Body, которые отвечают за конфигурацию отдельных частей автомобиля class Doors < getNumberOfDoors(type) < return type === 'truck' ? 2 : 4; >> class Body < createBody(type) < return type === 'truck'; >> // Далее создаем класс Car, который и является нашим фасадом. В методе creacteCar мы создали экземпляры классов и вызвали нужные методы у них, чтобы получить готовую конфигурацию автомобиля. Таким образом мы оградили пользователя от нужды понимания всех классов и их взаимодействия. Взамен этого предложили удобный интерфейс class Car < createCar(type) < const doors = new Doors().getNumberOfDoors(type); const body = new Body().createBody(type); return ; > > console.log(new Car().createCar('truck')); // < doors: 2, body: true >console.log(new Car().createCar('sport')); //
Adapter (Адаптер)
Согласно Википедии, Adapter — структурный шаблон проектирования, предназначенный для организации использования функций объекта, недоступного для модификации, через специально созданный интерфейс.
Проще говоря, Adapter позволяет объектам с несовместимыми интерфейсами работать вместе.
Еще проще: европейские розетки отличаются от английских, поэтому, приезжая в Лондон, туристы обязательно берут переходник, или адаптер.
Когда нужен: часто используется, если мы работаем со сторонней библиотекой или компонентом, доступ к изменениями методов которого у нас отсутствует. Или когда есть несколько сторонних систем, доступ к которым должен осуществляться единообразно через одинаковый интерфейс.
Существует два варианта этого паттерна:
- предоставляет возможность работать при помощи интерфейса/протокола, который наши объекты должны реализовать;
- дает доступ при помощи отдельного класса-адаптера, если нам необходимо внедрить более сложную логику преобразований.
Выбор той или иной реализации этого шаблона зависит от предпочтений разработчика и сложности кода, который необходимо адаптировать.
Как создать:
- Создаем класс Адаптера.
- В этом классе реализуем метод, который принимает на входе объект в незнакомом формате, а возвращает объект нужного нам формата.
- В этом методе реализуем логику преобразования объекта одного формата в другой.
Пример реализации:
interface Chief < Object makeBreakfast(); Object makeDinner(); >public class Plumber < // Adaptee public Object getScrewNut() < . >public Object getGasket() < . >> public class ChiefAdapter extends Plumber implements Chief < // Adapter public Object makeBreakfast() < return getGasket(); >public Object makeDinner() < return getScrewNut(); >> public class Client < // Client public static void eat(Object dish) < . >public static void main(String[] args) < Chief ch = new ChiefAdapter(); eat(ch.makeBreakfast()); eat(ch.makeDinner()); >>
// У нас есть класс калькулятора, который реализует единственный метод "operation" class Calculator < operation(num1, num2, operation) < switch (operation) < case 'multiplication': return num1 * num2; case 'division': return num1 / num2; default: return NaN; >> > // Допустим нам нужно создать улучшенную версию калькулятора class NewCalculator < add(num1, num2) < return num1 + num2; >div(num1, num2) < return num1 / num2; >mult(num1, num2) < return num1 * num2; >> // Но возникает проблема, что нет обратной совместимости со старым калькулятором, как раз в этом нам и поможет адаптер. Мы адаптируем новый калькулятор под функционал старого class CalculatorAdapter < constructor() < this.calculator = new NewCalculator(); >operation(num1, num2, operation) < switch (operation) < case "add": return this.calculator.add(num1, num2); case "multiplication": return this.calculator.mult(num1, num2); case "division": return this.calculator.div(num1, num2); default: return NaN; >> > // Обратите внимание, мы используем адаптер вместо старого калькулятора const calcAdapter = new CalculatorAdapter(); const sumAdapter = calcAdapter.operation(2, 2, "multiplication"); console.log(sumAdapter); // А новый функционал используем от экземпляра класса нового калькулятора const calculator = new Calculator(); const sum = calculator.mult(2, 2); console.log(sum);
Decorator (Декоратор)
Согласно Википедии, Decorator — структурный шаблон проектирования, предназначенный для динамического подключения дополнительного поведения к объекту.
Проще говоря, паттерн позволяет добавлять объектам новые функции с помощью обертки без создания отдельного класса.
Еще проще: пример декоратора из жизни — это подключение мышки к ноутбуку, то есть вы добавляете новую функцию устройству, не меняя его.
Когда нужен: у этого паттерна широкая область применения. Им пользуются каждый раз, когда нужно добавить логику в уже созданные объекты и библиотеки, менять которые нельзя.
Структура:
- Компонент с заданным интерфейсом
- Декоратор, который оборачивает компонент и добавляет новое поведение.
Как создать:
- Создаем интерфейс. В нем перечисляем методы библиотеки, с которой будет работать декоратор.
- Создаем класс, реализующий этот интерфейс. В нем прописываем логику обращения к объекту, который скрывается за декоратором, и добавляем свою кастомную логику.
Пример реализации:
public interface InterfaceComponent < void doOperation(); >class MainComponent implements InterfaceComponent < public void doOperation() < System.out.print("World!"); >> abstract class Decorator implements InterfaceComponent < protected InterfaceComponent component; public Decorator (InterfaceComponent c) < component = c; >public void doOperation() < component.doOperation(); >public void newOperation() < System.out.println("Do Nothing"); >> class DecoratorComma extends Decorator < public DecoratorComma(InterfaceComponent c) < super(c); >public void doOperation() < System.out.print(","); super.doOperation(); >public void newOperation() < System.out.println("New comma operation"); >> class DecoratorHello extends Decorator < public DecoratorHello(InterfaceComponent c) < super(c); >public void doOperation() < System.out.print("Hello"); super.doOperation(); >public void newOperation() < System.out.println("New hello operation"); >> class Main < public static void main (String. s) < Decorator c = new DecoratorHello(new DecoratorComma(new MainComponent())); c.doOperation(); // Результат выполнения программы "Hello,World!" c.newOperation(); // New hello operation >>
// создаем класс, с набором свойств и методов class Car < constructor(cost) < this.cost = cost; >getCost() < return `Стоимость автомобиля: $`; > > // далее создаем декоратор, который принимает экземпляр класса // и расширяет его, в данном случае добавляем свойство color const colorCar = (car, color) => < car.color = color; return car; >const car = new Car(16000); // оборачиваем инстанс car в декоратор, и получаем расширенный класс colorCar(car, 'orange') console.log(car); // Car < cost: 16000, color: 'orange' >console.log(car.getCost()); // Стоимость автомобиля: 16000
Заключение
Паттерны проектирования — это решения распространенных проблем при разработке кода. Их знание и использование позволяет экономить время, используя готовые решения, стандартизировать код и повысить общий словарь.
В зависимости от того, какие задачи решают паттерны проектирования, они делятся на три вида: порождающие, структурные и поведенческие.
Структурные паттерны связаны с композицией объектов или тем, как сущности могут использовать друг друга. Три самых популярных из них:
Facade, или Фасад, предоставляет упрощенный интерфейс для сложной системы. Используется в библиотеках и позволяет описать их так, чтобы пользователю не нужно было вникать в их реализацию.
Adapter, или Адаптер позволяет объектам с несовместимыми интерфейсами работать вместе. Используется, если мы работаем со сторонней библиотекой или компонентом, доступ к изменениями методов которого у нас отсутствует.
Decorator, или Декоратор, позволяет добавлять объектам новые функции с помощью обертки без создания отдельного класса. Им пользуются каждый раз, когда нужно добавить логику в уже созданные объекты и библиотеки, менять которые нельзя.
В следующих статьях мы подробнее расскажем про поведенческие паттерны и разберем самые популярные из них.
Паттерны проектирования в Java [Часть 1]

![Паттерны проектирования в Java [Часть 1] - 1](https://cdn.javarush.com/images/article/c35ac27e-86a9-49b3-bb23-9df463b38eb8/1024.jpeg)
Это краткая статья по паттернам проектирования в Java. Реализации паттернов не будет, тут только список паттернов которые есть в java и их краткое содержание. Она будет полезна тем, кто уже в теме, для повторения и обобщения. Или напротив, для тех, кто первый раз подошёл к паттернам — для самого первого обзора темы, прежде, чем копнуть глубже. Паттерны проектирования (шаблоны проектирования) — это готовые к использованию решения часто возникающих в программировании задач. Это не класс и не библиотека, которую можно подключить к проекту, это нечто большее. Паттерны проектирования, подходящий под задачу, реализуется в каждом конкретном случае. Следует, помнить, что такой паттерн, будучи примененным неправильно или к неподходящей задаче, может принести немало проблем. Тем не менее, правильно примененный паттерн поможет решить задачу легко и просто. Типы паттернов:
- порождающие
- структурные
- поведенческие
Порождающие паттерны предоставляют механизмы инициализации, позволяя создавать объекты удобным способом. Структурные паттерны определяют отношения между классами и объектами, позволяя им работать совместно. Поведенческие паттерны используются для того, чтобы упростить взаимодействие между сущностями. Порождающие:
- Singleton (Одиночка) — ограничивает создание одного экземпляра класса, обеспечивает доступ к его единственному объекту.
- Factory (Фабрика) — используется, когда у нас есть суперкласс с несколькими подклассами и на основе ввода, нам нужно вернуть один из подкласса.
- Abstract Factory (Абстрактная фабрика) — используем супер фабрику для создания фабрики, затем используем созданную фабрику для создания объектов.
- Builder (Строитель) — используется для создания сложного объекта с использованием простых объектов. Постепенно он создает больший объект от малого и простого объекта.
- Prototype (Прототип) — помогает создать дублированный объект с лучшей производительностью, вместо нового создается возвращаемый клон существующего объекта.
Структурные:
- Adapter (Адаптер) — это конвертер между двумя несовместимыми объектами. Используя паттерн адаптера, мы можем объединить два несовместимых интерфейса.
- Composite (Компоновщик) — использует один класс для представления древовидной структуры.
- Proxy (Заместитель) — представляет функциональность другого класса.
- Flyweight (Легковес) — вместо создания большого количества похожих объектов, объекты используются повторно.
- Facade (Фасад) — беспечивает простой интерфейс для клиента, и клиент использует интерфейс для взаимодействия с системой.
- Bridge (Мост) — делает конкретные классы независимыми от классов реализации интерфейса.
- Decorator (Декоратор) — добавляет новые функциональные возможности существующего объекта без привязки его структуры.
Поведенческие:
- Template Method (Шаблонный метод) — определяющий основу алгоритма и позволяющий наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.
- Mediator (Посредник) — предоставляет класс посредника, который обрабатывает все коммуникации между различными классами.
- Chain of Responsibility (Цепочка обязанностей) — позволяет избежать жесткой зависимости отправителя запроса от его получателя, при этом запрос может быть обработан несколькими объектами.
- Observer (Наблюдатель) — позволяет одним обьектам следить и реагировать на события, происходящие в других объектах.
- Strategy (Стратегия) — алгоритм стратегии может быть изменен во время выполнения программы.
- Command (Команда) — интерфейс команды объявляет метод для выполнения определенного действия.
- State (Состояние) — объект может изменять свое поведение в зависимости от его состояния.
- Visitor (Посетитель) — используется для упрощения операций над группировками связанных объектов.
- Interpreter (Интерпретатор) — определяет грамматику простого языка для проблемной области.
- Iterator (Итератор) — последовательно осуществляет доступ к элементам объекта коллекции, не зная его основного представления.
- Memento (Хранитель) — используется для хранения состояния объекта, позже это состояние можно восстановить.
Проходя курс JavaRush вы встретите пару паттернов из этого списка. Рекомендую задачи по паттернам: 1522, 1530, 1631, big01, 2912, 3107. Разумное использование паттернов проектирования приводит к повышению надежности обслуживания кода, поскольку в дополнение к тому, чтобы быть хорошим решением общей проблемы, паттерны проектирования могут быть распознаны другими разработчиками, что уменьшает время при работе с определенным кодом. Паттерны проектирования в Java [Часть 2]
Паттерны проектирования
Данной статьей мы начинаем серию статей, посвященных паттернам проектирования.
Статьи рассчитаны на тех, кто уже хорошо знает ООП.
Что такое паттерны в программировании
Ну, что ж, давайте сначала разберемся что такое паттерн. А затем плавно перейдем к такому понятию как «паттерны в программировании».
Паттерн — это повторяющийся элемент в различных сферах жизни.
Пример 1: окрас тигра — это паттерн.

Пример 2: Коробка передач — это паттерн.

В программировании, хотя каждая задача и каждая программа уникальна, у многих из них все же есть общие черты. Разработчики заметили эти закономерности, и выделили те, что наиболее часто встречаются, в паттерны (шаблоны проектирования, шаблоны программирования). В паттернах предлагается в том числе наиболее оптимальные способы реализации той или иной задачи.
Теперь, вместо того чтобы выдумывать велосипед заново, можно воспользоваться знаниями людей, которые уже решали эти проблемы до нас. Таким образом:
Шаблон проектирования / шаблон программирования / паттерн — это типичные способы решения часто возникающих задач в сфере разработки ПО.

ВАЖНО:
Паттерн — это не готовое решение, которое можно откуда-то скопировать и вставить в Вашу программу. Это только общие принципы, которые надо уметь правильно применить.
Мне надо знать паттерны?
- Паттерны очень часто применяются на практике. Конечно, для начинающих программистов понимание паттернов не всегда заходит легко. Так что наберитесь терпения и учим, учим, учим.
- Паттерны часто спрашивают на собеседованиях.
- И самое главное — паттерны предлагают Вам готовые решения. Они помогут Вам сохранить время и усилия, а качество программы повысится.
Откуда они взялись
Хотя сама идея паттернов далеко не новая, популярной она стала после выхода книги «Приёмы объектно-ориентированного проектирования. Паттерны проектирования«. Это произошло в 1994 году. С тех пор мир захватила «шаблономания»
Какие они бывают
Есть основные три категории паттернов:
- Порождающие (Creational Design Patterns)

Эти шаблоны что-то создают. Например, «как создать объект, который нельзя изменить»? «Как создать класс, который будет создавать новые объекты других классов?»?
- Структурные (Structural Design Patterns)

Отвечают за иерархию классов и интерфейсов. Например, «как заставить объекты с несовместимыми интерфейсами работа вместе»?
- Поведенческие (Behavioral Design Patterns)

Помогает добиться нужного поведения от объектов. Например, «как сделать так, чтобы объекты одного класса следили за изменениями в других классах и реагировали на них»?
Из чего состоит паттерн?
- Имя
- Задача, которую решает паттерн
- Решение:
- Структуры классов, составляющих решение;
- Примера на одном из языков программирования;
А конкретнее?
Существует 23 классических шаблона проектирования, с которых все и началось. В настоящий момент паттернов намного больше — минимум в 2-3 раза больше.
Здесь о каждом из них мы, конечно, говорить не будем — это много Но мы расскажем об основных паттернах в будущих статьях.
Самыми-самыми «базовыми» шаблонами проектирования можно назвать следующие:
С них можно начинать изучение паттернов. Ниже в этой статье Вы найдете ссылочки на статьи по этим паттернам.
Что следует знать
Одинаковые ли шаблоны для всех языков программирования?
Да, в целом понятия паттернов не привязано к конкретному языку. Тем не менее, когда говорят о паттернах, чаще всего имеют ввиду объектно-ориентированные языки программирования.
Паттернов очень много. Более того, кроме трех основных категорий, которые мы описали выше, есть и другие — шаблоны параллельного программирования, архитектурные шаблоны проектирования, и даже антипаттерны!
Да, конечно. Останется только всем о нем рассказать
Это все, что мы хотели Вам рассказать в данной статье.
Статьи, посвященные конкретным паттернам, Вы найдете по этим ссылочкам:
- Паттерн Singleton — Часть 1
- Паттерн Singleton — Часть 2
- Паттерн Builder
- Паттерн Factory — Часть 1
Надеемся, наша статья была Вам полезна. Также есть возможность записаться на наши курсы. Детальнее у на сайте.
- ← Правила наследования в Java
- Методы в Java →
Какие существуют паттерны проектирования на Java
Изучите паттерны проектирования Java для создания гибких и модульных систем с этим обзором порождающих, структурных и поведенческих паттернов!
Алексей Кодов
Автор статьи
7 июня 2023 в 16:12Паттерны проектирования — это проверенные и эффективные решения для определенных проблем, которые возникают при проектировании программного обеспечения. В Java существуют различные паттерны проектирования, которые можно классифицировать по следующим категориям:
1. Порождающие паттерны
Эти паттерны относятся к процессу создания объектов. Они обеспечивают гибкость и повторное использование кода при создании объектов. Вот некоторые из них:
- Singleton : Обеспечивает создание только одного экземпляра класса и предоставление глобальной точки доступа к этому экземпляру.
- Фабричный метод : Определяет интерфейс для создания объектов, позволяя подклассам выбирать, какой класс инстанциировать.
- Абстрактная фабрика : Предоставляет интерфейс для создания семейств взаимосвязанных объектов без указания их конкретных классов.
- Прототип : Позволяет создавать новые объекты путем копирования существующих объектов (прототипов), вместо создания новых объектов с нуля.
2. Структурные паттерны
Структурные паттерны определяют отношения между классами и объектами, создавая более сложные структуры. Вот некоторые из них:
- Адаптер : Позволяет объектам с несовместимыми интерфейсами работать вместе.
- Мост : Разделяет абстракцию и реализацию, так что они могут развиваться независимо друг от друга.
- Компоновщик : Объединяет объекты в древовидные структуры для представления иерархии частей-целого.
- Декоратор : Добавляет новые функции к существующему объекту без изменения его структуры.
3. Поведенческие паттерны
Поведенческие паттерны определяют обязанности и взаимодействие между объектами. Вот некоторые из них:
- Цепочка обязанностей ⛓️: Позволяет передавать запросы по цепочке обработчиков, пока один из них не обработает запрос.
- Команда : Инкапсулирует запрос в виде объекта, что позволяет параметризовать клиентов с различными запросами.
- Итератор : Предоставляет способ последовательного доступа к элементам объекта-агрегата без раскрытия его внутренней структуры.
- Наблюдатель : Определяет отношение «один-ко-многим» между объектами, так что при изменении состояния одного объекта автоматически уведомляются все зависимые объекты.
Java-разработчик: новая работа через 11 месяцев
Получится, даже если у вас нет опыта в IT
Заключение
Это лишь краткий обзор паттернов проектирования, доступных в Java. Использование этих паттернов может помочь вам создавать более гибкие, модульные и повторно используемые системы. Чтобы освоить их, практика и опыт являются ключевыми факторами. Удачи в изучении паттернов проектирования!
