Паттерны проектирования
Данной статьей мы начинаем серию статей, посвященных паттернам проектирования.
Статьи рассчитаны на тех, кто уже хорошо знает ООП.
Что такое паттерны в программировании
Ну, что ж, давайте сначала разберемся что такое паттерн. А затем плавно перейдем к такому понятию как «паттерны в программировании».
Паттерн — это повторяющийся элемент в различных сферах жизни.
Пример 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
Предположим, нам поставили задачу написать программу, которая должна нарисовать трехцветный флаг России. При этом она будет действовать по следующему алгоритму:
- нарисовать флагшток
- нарисовать первую полосу белого цвета
- нарисовать вторую полосу синего цвета
- нарисовать третью полосу красного цвета
1. Идем напролом
Затея выглядит просто:class Main < public static void main(String[] args) < System.out.println("Рисуем флаг России:"); System.out.println("Флагшток нарисован"); System.out.println("Полоса белого цвета нарисована"); System.out.println("Полоса синего цвета нарисована"); System.out.println("Полоса красного цвета нарисована"); >>
Результат выполнения программы:
Наша программа делает то, что требуется. Далее заказчик попросил написать код для рисования еще одного трехцветного флага — флага Нидерландов.
Добавим несколько строк кода:
class Main < public static void main(String[] args) < System.out.println("Рисуем флаг России:"); System.out.println("Флагшток нарисован"); System.out.println("Полоса белого цвета нарисована"); System.out.println("Полоса синего цвета нарисована"); System.out.println("Полоса красного цвета нарисована"); System.out.println("\nРисуем флаг Нидерландов:"); System.out.println("Флагшток нарисован"); System.out.println("Полоса красного цвета нарисована"); System.out.println("Полоса белого цвета нарисована"); System.out.println("Полоса синего цвета нарисована"); >>
Результат выполнения программы:
Мы выполнили и это задание — программа рисует необходимые заказчику флаги.
Но, наш код ужасен! Мы не используем объектно-ориентированный подход, и у нас нарушен DRY-принцип — код дублируется.
2. Используем ООП
Давайте создадим для каждого флага свой класс: RussianFlag и NetherlandsFlag. Рисование конкретным цветом вынесем в методы, которые поместим в класс Colors, что позволит избавиться от дублирования кода.
Методы рисования красками сделаем статическими (чтобы не создавать экземпляр):
class Colors < static void paintWhiteColor() < System.out.println("Полоса белого цвета нарисована"); >static void paintBlueColor() < System.out.println("Полоса синего цвета нарисована"); >static void paintRedColor() < System.out.println("Полоса красного цвета нарисована"); >>
Классы для рисования флага России и Нидерландов выглядят так:
class RussianFlag < void drawFlag() < drawFlagpole(); Colors.paintWhiteColor(); Colors.paintBlueColor(); Colors.paintRedColor(); >private void drawFlagpole() < System.out.println("Флагшток нарисован"); >>
class NetherlandsFlag < void drawFlag() < drawFlagpole(); Colors.paintRedColor(); Colors.paintWhiteColor(); Colors.paintBlueColor(); >private void drawFlagpole() < System.out.println("Флагшток нарисован"); >>
Класс Main теперь выглядит так:
class Main < public static void main(String[] args) < System.out.println("Рисуем флаг России:"); RussianFlag russianFlag = new RussianFlag(); russianFlag.drawFlag(); System.out.println("\nРисуем флаг Нидерландов:"); NetherlandsFlag netherlandsFlag = new NetherlandsFlag(); netherlandsFlag.drawFlag(); >>
Запустив это код, мы получим тот же самый результат, что и ранее — будут нарисованы два флага.
Код уже стал лучше, но мы можем вынести рисование флагштока в отдельный класс Flagpole, что бы метод не дублировался. А уже от этого класса будут наследоваться классы для рисования конкретных флагов.
Класс Flagpole выглядит так:
class Flagpole < static void drawFlagpole() < System.out.println("Флагшток нарисован"); >>
Внесем изменения в классы RussianFlag и NetherlandsFlag: добавим extends Flagpole и уберем метод drawFlagpole:
class RussianFlag extends Flagpole < void drawFlag() < drawFlagpole(); Colors.paintWhiteColor(); Colors.paintBlueColor(); Colors.paintRedColor(); >>
class NetherlandsFlag extends Flagpole < void drawFlag() < drawFlagpole(); Colors.paintRedColor(); Colors.paintWhiteColor(); Colors.paintBlueColor(); >>
Снова запустив класс Main, мы получим тот же самый результат — два требуемых флага.
3. Реализуем Шаблонный методВнезапно мы понимаем, что каждый раз действуем по одному и тому же алгоритму: рисуем флагшток, верхнюю часть флага, среднюю и нижнюю. Этот алгоритм неизменен для рисования флагов с тремя полосами. А что, если нам сделать методы для рисования полос флага абстрактными, а их реализацию делегировать (поручить) классам конкретных флагов? И даже сам алгоритм рисования флага поместить в абстрактный класс в метод drawFlag. А что бы его нельзя было переопределить (изменить) при наследовании, пометить его как final.
Класс Flagpole становится ненужным, поскольку рисование флагштока также можно перенести в абстрактный класс. Итак, класс AbstractThreeRowsFlag будет выглядеть следующим образом:
abstract class AbstractThreeRowsFlag < abstract void drawUpperLevel(); abstract void drawMiddleLevel(); abstract void drawBottomLevel(); final void drawFlag() < drawFlagpole(); drawUpperLevel(); drawMiddleLevel(); drawBottomLevel(); >private void drawFlagpole() < System.out.println("Флагшток нарисован"); >>
Метод drawFlag(), содержащий алгоритм рисования флага, является шаблонным методом. Он так называется потому, что задает скелет в виде последовательности шагов, которые будут переопределять (реализовывать), каждый по своему, наследники AbstractThreeRowsFlag. Его подклассы не смогут изменить последовательность шагов, т.к. шаблонный метод помечен, как final и не наследуется.
Как вы уже поняли, для рисования флагов мы только что использовали паттерн под названием «Шаблонный метод» (Template Method), благодаря которому мы избавились от дублирования кода, а так же повысили его универсальность и переиспользуемость.
Обратите внимание, не все методы в шаблонном методе являются абстрактными, например drawFlagpole (рисование флагштока). Из этого можно сделать вывод, что шаблонный метод не обязательно должен состоять только из абстрактных методов, которые необходимо переопределять в подклассах. Он может содержать переменные, какую-то внутреннюю логику и т. д. Но очень часто новички, читая материал по данному паттерну, ошибочно полагают, что шаблонный метод — это метод, который состоит только из абстрактных методов и ничего другого содержать не может. Это и не удивительно, в интернете полно статей с примерами таких методов, что сбивает с толку.
Вернемся к нашим флагам, классы для рисования которых уже выглядят так (про @Override можно прочитать в нашей прошлой статье):
class RussianFlag extends AbstractThreeRowsFlag < @Override void drawUpperLevel() < Colors.paintWhiteColor(); >@Override void drawMiddleLevel() < Colors.paintBlueColor(); >@Override void drawBottomLevel() < Colors.paintRedColor(); >>
class NetherlandsFlag extends AbstractThreeRowsFlag < @Override void drawUpperLevel() < Colors.paintRedColor(); >@Override void drawMiddleLevel() < Colors.paintWhiteColor(); >@Override void drawBottomLevel() < Colors.paintBlueColor(); >>
Класс Main теперь выглядит так:
class Main < public static void main(String[] args) < System.out.println("Рисуем флаг России:"); AbstractThreeRowsFlag russianFlag = new RussianFlag(); russianFlag.drawFlag(); System.out.println("\nРисуем флаг Нидерландов:"); AbstractThreeRowsFlag netherlandsFlag = new NetherlandsFlag(); netherlandsFlag.drawFlag(); >>
Теперь можно нарисовать флаг Югославии, Германии (добавив к нашему набору цветов необходимые: черный и желтый) и любые другие подобные трехцветные флаги, применяя ООП и максимально переиспользуя существующий код.
Какие существуют паттерны проектирования на Java
Изучите паттерны проектирования Java для создания гибких и модульных систем с этим обзором порождающих, структурных и поведенческих паттернов!
Алексей Кодов
Автор статьи
7 июня 2023 в 16:12Паттерны проектирования — это проверенные и эффективные решения для определенных проблем, которые возникают при проектировании программного обеспечения. В Java существуют различные паттерны проектирования, которые можно классифицировать по следующим категориям:
1. Порождающие паттерны
Эти паттерны относятся к процессу создания объектов. Они обеспечивают гибкость и повторное использование кода при создании объектов. Вот некоторые из них:
- Singleton : Обеспечивает создание только одного экземпляра класса и предоставление глобальной точки доступа к этому экземпляру.
- Фабричный метод : Определяет интерфейс для создания объектов, позволяя подклассам выбирать, какой класс инстанциировать.
- Абстрактная фабрика : Предоставляет интерфейс для создания семейств взаимосвязанных объектов без указания их конкретных классов.
- Прототип : Позволяет создавать новые объекты путем копирования существующих объектов (прототипов), вместо создания новых объектов с нуля.
2. Структурные паттерны
Структурные паттерны определяют отношения между классами и объектами, создавая более сложные структуры. Вот некоторые из них:
- Адаптер : Позволяет объектам с несовместимыми интерфейсами работать вместе.
- Мост : Разделяет абстракцию и реализацию, так что они могут развиваться независимо друг от друга.
- Компоновщик : Объединяет объекты в древовидные структуры для представления иерархии частей-целого.
- Декоратор : Добавляет новые функции к существующему объекту без изменения его структуры.
3. Поведенческие паттерны
Поведенческие паттерны определяют обязанности и взаимодействие между объектами. Вот некоторые из них:
- Цепочка обязанностей ⛓️: Позволяет передавать запросы по цепочке обработчиков, пока один из них не обработает запрос.
- Команда : Инкапсулирует запрос в виде объекта, что позволяет параметризовать клиентов с различными запросами.
- Итератор : Предоставляет способ последовательного доступа к элементам объекта-агрегата без раскрытия его внутренней структуры.
- Наблюдатель : Определяет отношение «один-ко-многим» между объектами, так что при изменении состояния одного объекта автоматически уведомляются все зависимые объекты.
Java-разработчик: новая работа через 11 месяцев
Получится, даже если у вас нет опыта в ITЗаключение
Это лишь краткий обзор паттернов проектирования, доступных в Java. Использование этих паттернов может помочь вам создавать более гибкие, модульные и повторно используемые системы. Чтобы освоить их, практика и опыт являются ключевыми факторами. Удачи в изучении паттернов проектирования!
Что такое паттерны проектирования в Java
Изучите паттерны проектирования в Java, их типы и примеры применения для создания гибких и масштабируемых систем!
Алексей Кодов
Автор статьи
7 июня 2023 в 16:12Паттерны проектирования — это проверенные и широко используемые решения для распространенных проблем, возникающих в процессе проектирования программного обеспечения. Они представляют собой шаблоны, которые можно адаптировать и применять в различных ситуациях.
В Java существует множество паттернов проектирования, которые можно разделить на три основных типа:
- Порождающие паттерны: Эти паттерны связаны с процессом создания объектов и обеспечивают гибкость и повторное использование кода. Примеры таких паттернов: Singleton, Factory Method, Abstract Factory, Builder, Prototype.
- Структурные паттерны: Они определяют способы композиции классов и объектов для создания новых структур. Примеры структурных паттернов: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy.
- Поведенческие паттерны: Эти паттерны определяют взаимодействие между объектами и облегчают обмен информацией между ними. Примеры поведенческих паттернов: Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor.
Java-разработчик: новая работа через 11 месяцев
Получится, даже если у вас нет опыта в ITПримеры паттернов проектирования в Java
Давайте рассмотрим некоторые популярные паттерны проектирования на примерах Java-кода.
Singleton
Singleton — это порождающий паттерн, который гарантирует, что класс имеет только один экземпляр и предоставляет глобальную точку доступа к этому экземпляру.
public class Singleton < private static Singleton instance; private Singleton() < >public static synchronized Singleton getInstance() < if (instance == null) < instance = new Singleton(); >return instance; > >
Adapter
Adapter — это структурный паттерн, который позволяет объектам с несовместимыми интерфейсами работать вместе.
interface EuropeanPlug < void useEuropeanPlug(); >class BritishPlug < void useBritishPlug() < System.out.println("Using British plug"); >> class PlugAdapter implements EuropeanPlug < private BritishPlug britishPlug; public PlugAdapter(BritishPlug britishPlug) < this.britishPlug = britishPlug; >@Override public void useEuropeanPlug() < britishPlug.useBritishPlug(); >>
Observer
Observer — это поведенческий паттерн, который определяет отношение «один-ко-многим» между объектами. Когда один объект (субъект) изменяется, все зависимые объекты (наблюдатели) уведомляются об этом и автоматически обновляются.
import java.util.ArrayList; import java.util.List; interface Observer < void update(String news); >class NewsChannel implements Observer < private String news; @Override public void update(String news) < this.news = news; System.out.println("News updated: " + news); >> class NewsAgency < private List<Observer> observers = new ArrayList<>(); public void addObserver(Observer observer) < observers.add(observer); >public void removeObserver(Observer observer) < observers.remove(observer); >public void notifyObservers(String news) < for (Observer observer : observers) < observer.update(news); >> >
Использование паттернов проектирования в Java помогает создавать гибкие, масштабируемые и понятные системы. Изучение этих паттернов является важным шагом в становлении опытным Java-разработчиком.