Рубріки: HighloadТеория

Паттерны проектирования: их виды, особенности и преимущества

Андрій Денисенко

Что такое паттерн проектирования

Шаблоны или паттерны проектирования (от англ. Design Patterns) — это типовые решения распространенных проблем при проектировании программного обеспечения. Они используются как руководства, которые помогают инженерам создавать проекты в соответствии с передовыми рекомендациями.

Шаблоны проектирования — это «описания взаимодействующих объектов и классов, которые созданы для решения общей проблемы проектирования в конкретном контексте» (из книги «Шаблоны проектирования: повторно используемые элементы объектно-ориентированного программного обеспечения» — Эрих Гамма и др.).

Хорошие шаблоны проектирования помогают повысить гибкость вашего ПО и делают его пригодным для повторного использования.

Описания большинства шаблонов очень формальны, поэтому их можно применять в различных контекстах. Но в этих описаниях есть общие разделы:

  • назначение вкратце описывает проблему и ее решение;
  • мотивация подробно описывает проблему и ее решение, которое стало возможным благодаря шаблону;
  • структура классов демонстрирует компоненты шаблона и взаимосвязи между ними;
  • пример кода на одном из популярных языков программирования, благодаря которому можно проще воспринять идею, лежащую в основе шаблона.

В некоторых каталогах шаблонов даются другие полезные сведения, например, о применимости шаблона, этапах его реализации и взаимоотношениях с другими шаблонами.

История появления шаблонов проектирования

Как появляются шаблоны? Когда одно и то же решение успешно применяется в различных проектах, кто-то дает ему название и подробное описание.

Шаблоны проектирования позволяют решать распространенные проблемы в объектно-ориентированном проектировании (ООП).

Концепцию шаблонов впервые описал Кристофер Александр в книге A Pattern Language: Towns, Buildings, Construction («Язык шаблонов: города, здания, конструкции»). В ней описан «язык» проектирования городской архитектуры. Единицы этого языка — шаблоны. Они указывают, какой высоты должны быть окна, сколько этажей должно быть в здании, какой должна быть площадь зон озеленения по соседству и так далее.

Эту идею подхватили четыре автора: Эрих Гамма, Джон Влиссидес, Ральф Джонсон и Ричард Хелм. Именно они в 1994 году опубликовали книгу «Шаблоны проектирования: повторно используемые элементы объектно-ориентированного программного обеспечения», в которой применили концепцию шаблонов проектирования к программированию.

В книге представлены 23 шаблона для решения различных задач объектно-ориентированного проектирования. Она очень быстро стала бестселлером. Из-за длинного названия ее стали называть «книгой банды четырех», что вскоре сократили до просто «книга GoF».

В соответствии со мнением авторов, шаблоны проектирования в основном основаны на следующих принципах ООП:

  • программирование на уровне интерфейсов, а не реализации;
  • композиция объектов вместо наследования.

С того времени были открыты десятки других шаблонов ООП. Подход с применением шаблонов стал очень популярным в других областях программирования, поэтому теперь существует множество других шаблонов за пределами сферы ОП.

Плюсы и минусы использования шаблонов проектирования

Плюсы

Даже если вы прежде работали, не имея представления о шаблонах, вы все равно реализовали некоторые из них, не зная об этом. Тогда зачем же тратить время на их изучение?

Шаблоны проектирования — это комплект испытанных и протестированных решений распространенных проблем в сфере проектирования ПО.

Даже если вы никогда не сталкивались с этими проблемами, знание шаблонов поможет вам, потому что вы научитесь решать проблемы разного рода, применяя принципы ООП.

Повышенная эффективность. В решении проблем разработчикам не приходится изобретать велосипед. Они применяют проверенные, надежные решения. Благодаря применению шаблонов их работа становится намного более эффективной.

Повторное использование. Шаблоны проектирования можно изменять для решения разнообразных проблем. Они не связаны с отдельной конкретной проблемой.

Отсутствие необходимости в рефакторинге кода. Поскольку шаблон проектирования — уже лучшее решение проблемы, можно обойтись без рефакторинга.

Меньший размер базы кода. Каждый шаблон помогает разработчикам ПО изменять работу системы без полного перепроектирования. Кроме того, как оптимальное решение, шаблон требует меньше кода.

Общий язык. Шаблоны проектирования определяют общий язык, на котором вы вместе со своей командой можете общаться эффективнее. Не нужно объяснять, что такое одиночка (или синглет), если вы знаете шаблон и его название.

Шаблоны проектирования предоставляют стандартизированную терминологию и относятся к отдельным сценариям. Например, применение шаблона одиночка означает использование единственного объекта, поэтому все разработчики, знакомые с этим шаблоном, будут использовать его в нужных случаях и смогут сообщить другим разработчикам, что программа использует именно этот шаблон.

Быстрая подготовка новых разработчиков. Шаблоны проектирования разрабатывались в течение долгого времени и теперь они — лучшее решение определенных проблем, с которыми сталкиваются разработчики. Изучение шаблонов поможет неопытным разработчикам быстро и с легкостью освоить проектирование ПО.

Намного проще подготовить нового разработчика к работе над проектом, в котором используются шаблоны проектирования, чем к работе над проектом без их использования.

Повышение квалификации. Знание шаблонов проектирования помогает вам находить сходства в коде.

Если вы знаете и понимаете различные шаблоны проектирования, то начинаете замечать их использование в коде. Это помогает, ведь вы уже представление о том, как использовать этот код.

Минусы

Шаблоны проектирования часто подвергаются критике. Рассмотрим типичные аргументы против их использования.

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

Неэффективность решений. Шаблоны — это попытка систематизировать подходы, которые уже нашли широкое применение. Если применять шаблоны буквально, без адаптации к контексту проекта, они не принесут пользы.

Неоправданное использование. Недостаточно просто знать и применять шаблоны проектирования. Нужно знать, когда и как их применять. В противном случае есть риск создания антишаблона, который либо не повышает эффективность, либо даже понижает ее. Иногда можно решить проблему проще.

Чрезмерное применение. При чрезмерном использовании шаблонов проектирования ваш проект станет чересчур сложным. Поэтому, прежде чем применять шаблон проектирования, нужно ясно и четко определить проблему.

Избыточность шаблонов. Многие шаблоны избыточны, потому что реализованы в языках программирования, например, как в Swift. Но это не значит, что реализованы все шаблоны.

Шаблоны проектирования — это отговорка лентяев, которые не хотят учить принципы ООП. А почему бы не выучить и одно, и другое? Четкое понимание принципов ООП обязательно поможет вам в разработке ПО. Но если вы уже знаете, что шаблон программирования хорошо решает определенную проблему, то зачем создавать решение с нуля?

Лучше сначала подробно разобраться в шаблонах и понять, зачем они нужны, чем пытаться уловить это на лету за день до дедлайна после обнаружения критической ошибки.

Отличие паттернов проектирования от схожих инструментов

Чтобы разобраться в шаблонах проектирования, нужно понять их суть. Для этого, кроме прочего, следует научиться отличать их от других инструментов.

  • Архитектурные шаблоны представляют собой решения часто встречающихся проблем архитектуры приложений в определенном контексте. Они описывают архитектуру в целом и взаимосвязь между подсистемами. Яркий пример архитектурного шаблона — «модель-представление-контроллер» (Model-View-Controller — MVC). До сих пор ведутся споры о том, являются ли такие шаблоны, как MVVM и MVC, архитектурными, то есть охватывают ли они архитектуру целого приложения или подсистемы. Поэтому архитектурные шаблоны — более широкое понятие, чем шаблоны проектирования, которые охватывают компоненты или части приложения. В архитектурных шаблонах могут использоваться несколько шаблонов проектирования.
  • Шаблоны часто путают с алгоритмами, потому что обе эти концепции описывают типовые решения широко известных задач. В то время как алгоритм всегда определяет четкую последовательность действий, которые нужно выполнить для достижения определенной цели, шаблон — более высокоуровневое описание решения. Код реализации одного и того же шаблона в разных программах будет разным.
  • В отличие от библиотеки или платформы, которые можно просто вставить в проект и использовать, шаблон проектирования — образец подхода к решению проблемы.

Итак, шаблоны проектирования описывают компонент или часть приложения на уровне подхода к решению типовой задачи.

Основные виды паттернов проектирования

Шаблоны проектирования различаются по сложности, уровню детализации и объему применимости к проектируемой системе целиком.

Самые простые и низкоуровневые шаблоны часто называют идиомами. Обычно они применяются к одному языку программирования.

Наиболее универсальными и высокоуровневые шаблоны — архитектурные. Их можно применить практически в любом языке. В отличие от других видов шаблонов, их можно использовать для проектирования архитектуры целого приложения.

Кроме того, шаблоны можно классифицировать по назначению. С этой точки зрения выделяют три типа шаблонов:

  1. порождающие шаблоны (Creational patterns) предоставляют механизмы создания объектов, и эти механизмы повышают гибкость кода и дают больше возможностей для его повторного использования;
  2. структурные шаблоны (Structural patterns) объясняют, как проводить сборку объектов и классов в более крупные структуры, сохраняя гибкость и эффективность этих структур;
  3. поведенческие шаблоны (Behavioral patterns) обеспечивают эффективную взаимосвязь между объектами и распределение обязанностей между ними.

Выделяют 23 классических шаблона проектирования, хотя на данный момент их выявлено намного больше.

Порождающие Структурные Поведенческие
Класс Фабричный метод Адаптер (класс) Интерпретатор
Шаблонный метод
Объект Абстрактная фабрика Строитель
Прототип
Одиночка
Адаптер (объект)
Мост
Компоновщик
Декоратор
Фасад
Легковес
Заместитель
Цепочка обязанностей
Команда
Итератор
Посредник
Снимок
Наблюдатель
Состояние
Стратегия
Посетитель

Основные шаблоны

Из 23-х классических шаблонов семь считаются самыми важными.

Это:

  • одиночка (Singleton);
  • фабричный метод (Factory Method);
  • фасад (Facade);
  • стратегия (Strategy);
  • наблюдатель (Observer);
  • строитель (Builder);
  • адаптер (Adapter).

Шаблон проектирования одиночка

Шаблон проектирования одиночка относится к порождающим шаблонам.

Одиночка (Singleton) — это класс, у которого может быть только один экземпляр, являющийся глобальной переменной с глобальным доступом.

Например, целесообразно реализовать карту сайта в виде одиночки, чтобы существовала только одна версия, к которой предоставляется глобальный доступ.

Кстати, другие шаблоны, например, фабричный метод, строитель и прототип, могут использовать шаблоны проектирования одиночка. Объекты с шаблоном фасада и состояния тоже часто бывают одиночками.

Если у вас есть или вам нужен только один экземпляр класса, это еще не значит, что следует использовать шаблон одиночка, чтобы заблокировать объект или предоставить к нему глобальный доступ.

Одиночка — это спорный шаблон, и есть мнение, что это даже антишаблон, которого следует избегать, потому что блокирование объекта ограничивает гибкость в будущем.

Давайте рассмотрим пример кода реализации одиночки.

1. Создаем класс-одиночку.

class SiteMap {

   // Create SiteMap object
   private static SiteMap instance = new SiteMap();

   // Declare private constructor
   // to avoid creating a class instance
   private SiteMap(){}

   // Get the only object available
   public static SiteMap getInstance(){
      return instance;
   }
   
    // Declare a method
   public void showMessage(){
      System.out.println("My Site Map!");
   }
}

2. Получаем единственный экземпляр класса-одиночки. Это невозможно сделать с помощью оператора new (SingleObject object = new SiteMap(); не сработает).

public class SingletonDemo {

   public static void main(String[] args) {
   
      // Get the only object available
      SiteMap object = SiteMap.getInstance();
      
      // Output the message
      object.showMessage();
   }
}

Шаблон проектирования фабричный метод

Фабричный метод — это порождающий шаблон проектирования. С его помощью можно создавать объекты с общим интерфейсом, за реализацию которого отвечают подклассы.

Фабричный метод обеспечивает слабое связывание и повторное использование кода. Это своего рода виртуальный конструктор, который работает с любым классом, реализующим интерфейс и обеспечивает большую свободу выбора реализации для подклассов. Когда возникает необходимость в новых классах, их можно добавить в фабрику.

Шаблон проектирования фабричный метод неприменим в простых сценариях. Это тот случай, когда есть риск излишнего усложнения процесса ради применения шаблона проектирования.

Давайте рассмотрим пример кода реализации фабричного метода.

В Java фабричный метод — это статический метод, который создает и возвращает объект классу, которому он принадлежит.

1. Создаем интерфейс.

interface Shape {
    double calculateArea();
}

2. Создаем конкретные классы, реализующие этот интерфейс.

class Rectangle implements Shape {

    double width;
    double height;
    
    Rectangle(double width, double height){
        this.width = width;
        this.height = height;
    }
    
    public double calculateArea() {
        return this.width * this.height;
    }
}

class Circle implements Shape {
    double radius;

    Circle(double radius){
        this.radius = radius;
    }

    public double calculateArea() {
        return Math.PI * (this.radius * this.radius);
    }
}

3. Проверяем работу шаблона.

public class FactoryMethodDemo{

    public static void main(String[] args){

      Rectangle r = new Rectangle(10, 20);
      Circle c = new Circle(25);
      
      System.out.println("Rectangle area is: " + r.calculateArea());
      System.out.println("Circle area is: " + c.calculateArea());
    }
}

Шаблон проектирования фасад

Шаблон проектирования фасад — это структурный шаблон, который предоставляет один интерфейс (класс) для доступа к большому объему кода или различным объектам.

Фасад скрывает сложности различных подсистем (часто упорядоченных в одном классе) за простым интерфейсом. Например, клиенту в сфере электронной коммерции нужна только одна точка взаимодействия с брендом, которая избавит от необходимости во взаимодействии с каждой системой по отдельности (запасы продукции, безопасность, обработка платежа, выполнение заказа и так далее). В этом случае фасад инкапсулирует все действия и системы «заказа» в одном интерфейсе, а клиент не имеет представления о том, что происходит за кулисами.

Фасад — важная концепция для слабосвязанной архитектуры микросервисов.

Пример кода реализации фасада

1. Создаем интерфейс.

// Declare the common interface
interface Meal {
   void cook();
}

2. Создаем классы, реализующие интерфейс.

// Declare classes implementing the same interface.

class Pizza implements Meal {

   @Override
   public void cook() {
      System.out.println("Cooking a pizza.");
   }
}

class Lasagna implements Meal {

   @Override
   public void cook() {
      System.out.println("Cooking a lasagna.");
   }
}

class Soup implements Meal {

   @Override
   public void cook() {
      System.out.println("Cooking a soup.");
   }
}

3. Создаем класс фасада.

// Create a facade class

class MealMaker {
   private Meal pizza;
   private Meal lasagna;
   private Meal soup;

   public MealMaker() {
      pizza = new Pizza();
      lasagna = new Lasagna();
      soup = new Soup();
   }

   public void cookPizza(){
      pizza.cook();
   }
   public void cookLasagna(){
      lasagna.cook();
   }
   public void cookSoup(){
      soup.cook();
   }
}

4. Применяем фасад для вызова методов.

// Use the facade to cook three Meals.

public class FacadeDemo {
   public static void main(String[] args) {
      MealMaker MealMaker = new MealMaker();

      MealMaker.cookPizza();
      MealMaker.cookLasagna();
      MealMaker.cookSoup();      
   }
}

Шаблон проектирования стратегия

Шаблон проектирования стратегия — это поведенческий шаблон проектирования. Второе его название — политика. В шаблоне стратегии взаимозаменяемые алгоритмы инкапсулированы в «семейство», из которого выбирается один алгоритм в соответствии с необходимостью.

Например, если семейство алгоритмов связано с сортировкой продуктов на веб-сайте электронной коммерции (по размеру, цвету, цене и так далее), то стратегия выбирается в зависимости от действий клиента.

Шаблон проектирования «стратегия» очень эффективен в применении к персонализации стратегий маркетинга, реагировании на расположение клиента, его действия или введенные данные. Он обеспечивает персональное взаимодействие для каждого пользователя.

Рассмотрим пример кода реализации стратегии.

1. Создаем интерфейс.

// Declare the interface

interface Strategy {
   double doOperation(double x, double y);
}

2. Создаем конкретные классы, которые используют интерфейс.

// Declare classes that implement the same interface.

class AddOperation implements Strategy{
   @Override
   public double doOperation(double x, double y) {
      return x + y;
   }
}

class SubstractOperation implements Strategy{
   @Override
   public double doOperation(double x, double y) {
      return x - y;
   }
}

class MultiplyOperation implements Strategy{
   @Override
   public double doOperation(double x, double y) {
      return x * y;
   }
}

class DivideOperation implements Strategy{
   @Override
   public double doOperation(double x, double y) {
      return x / y;
   }
}

3. Создаем класс контекста.

// Create context class

clas Context {
   private Strategy strategy;

   Context(Strategy strategy){
      this.strategy = strategy;
   }

   double executeStrategy(double x, double y){
      return strategy.doOperation(x, y);
   }
}

4. Применяем контекст.

// Use the Context to see how behaviour changes with the change of Strategy.

public class StrategyDemo {
   public static void main(String[] args) {
      Context context = new Context(new AddOperation());        
      System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

      context = new Context(new SubstractOperation());      
      System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

      context = new Context(new MultiplyOperation());       
      System.out.println("10 * 5 = " + context.executeStrategy(10, 5));

      context = new Context(new DivideOperation());       
      System.out.println("10 / 5 = " + context.executeStrategy(10, 5));
   }
}

5. Результат:

10 + 5 = 15.0
10 - 5 = 5.0
10 * 5 = 50.0
10 / 5 = 2.0

Шаблон проектирования наблюдатель

Шаблон проектирования наблюдатель — это поведенческий шаблон, который связывает объект (субъект) с зависимыми от него объектами (наблюдателями) по модели «один ко многим».

Когда какие-либо наблюдатели меняются, субъект получает уведомление об этом. Шаблон проектирования наблюдатель пригодится в программировании систем, основанных на событиях, например, для оповещения пользователя о новом комментарии в соцсети, отправки сообщения электронной почты при отправке товара и так далее.

Приведем пример кода реализации наблюдателя.

1. Объявим интерфейс субъекта.

import java.util.ArrayList;
import java.util.List;

// Declare the Subject interface

interface Subject{
    void add(Observer observer);
    void remove(Observer observer);
    void notify(Message message);
}

2. Объявим интерфейс наблюдателя.

// Declare the Observer interface

interface Observer{
    void update(Message message);
}

3. Объявим конкретный класс субъекта.

// Declare concrete Subject

class Publisher implements Subject {
     
    private List<Observer> observers = new ArrayList<>();
 
    @Override
    public void add(Observer o) {
        observers.add(o);
    }
 
    @Override
    public void remove(Observer o) {
        observers.remove(o);
    }
 
    @Override
    public void notify(Message m) {
        for(Observer o: observers) {
            o.update(m);
        }
    }
}

4. Объявим конкретные классы наблюдателей.

// Declare concrete Observers

class Subscriber1 implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("Subscriber 1 received a message: " + m.getContent());
    }
}

class Subscriber2 implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("Subscriber 2 received a message: " + m.getContent());
    }
}

5. Объявим объект состояния.

// Declare the state object

class Message 
{
    final String content;
     
    Message (String s) {
        this.content = s;
    }
 
    String getContent() {
        return content;
    }
}

6. Проверим взаимодействие.

// Test the communication

class ObserverDemo 
{
    public static void main(String[] args) 
    {
        Subscriber1 s1 = new Subscriber1();
        Subscriber2 s2 = new Subscriber2();
         
        Publisher p = new Publisher();
         
        p.add(s1);
        p.add(s2);
         
        p.notify(new Message("First Message")); // both observers will receive the message
         
        p.remove(s1);
         
        p.notify(new Message("Second Message")); // only the second observer will receive the message
    }
}

Шаблон проектирования строитель

Шаблон проектирования строитель — это порождающий шаблон, отделяющий создание объекта от представления. Он предоставляет больше возможностей контроля над процессами проектирования (пошагово), а также отделяет представление, чтобы можно было создать множество представлений объекта с использованием одного и того же кода.

Шаблон строитель создает объект за несколько последовательных шагов, причем только тех, которые необходимы для каждой итерации объекта.

Например, если есть класс со множеством полей, то возникает проблема «телескопических конструкторов». Очень трудно запомнить порядок следования полей и легко ошибиться, создавая код. С помощью шаблона проектирования строитель эта проблема решается так…

Переходим к примеру кода реализации строителя:

class User{

    // Required fields
    private String email;
    private String password;

    // Optional fields
    private String fullName;
    private int age;
    private String phoneNumber;

    public static class Builder {
        private User newUser;

        public Builder(){
            newUser = new User();
        }

        public Builder setEmail(String email){
            newUser.email = email;
            return this;
        }

        public Builder setPassword(String password){
            newUser.password = password;
            return this;
        }

        public Builder setFullName(String fullName){
            newUser.fullName = fullName;
            return this;
        }

        public Builder setAge(int age){
            newUser.age = age;
            return this;
        }

        public Builder setPhoneNumber(String phoneNumber){
            newUser.phoneNumber = phoneNumber;
            return this;
        }

        public User build(){
            return newUser;
        }

    }

    public void print(){
        System.out.print(
            "User:     " + this.fullName + "\n" +
            "Email:    " + this.email + "\n" +
            "Password: " + this.password + "\n" +
            "Age:      " + this.age + "\n" +
            "Phone:    " + this.phoneNumber + "\n");
        }
}

class BuilderDemo{
    public static void main(String[] args){
        User myUser = new User.Builder()
            .setEmail("name@example.com")
            .setPassword("secret123")
            .setFullName("New User")
            .setAge(30)
            .setPhoneNumber("555-55-55")
            .build();

        myUser.print();
    }
}

Шаблон проектирования адаптер

Шаблон проектирования адаптер — это оболочка, преобразующая один тип интерфейса в другой существующий тип интерфейса. Этот шаблон помогает обеспечить совместную работу несовместимых классов.

Шаблоны адаптеров полезны, когда нужно создать последовательный API из смешанных интерфейсов.

В приведенном ниже примере определяются два несовместимых интерфейса, создаются целевые объекты, которые реализуют эти интерфейсы, создается адаптер, приводящий второй объект к первому интерфейсу, и создается клиент, использующий целевые объекты.

Пример кода реализации адаптера:

// Declare the 1st interface
interface Transport
{
    void travel();
}

// Declare the class implementing the 1st interface
class Car implements Transport{
    public void travel(){
        System.out.println("Traveling by car");
    }
}

// Declare the client class
class Passenger{
    public void travel(Transport transport){
        transport.travel();
    }
}

// Declare the 2nd interface
interface Animal
{
    void walk();
}

// Declare the class implementing the 2nd interface
class Horse implements Animal{
    public void walk(){
        System.out.println("Riding a horse");
    }
}

// Declare the adapter
class HorseAdapter implements Transport
{
    Horse horse;
    public HorseAdapter(Horse horse)
    {
        this.horse = horse;
    }
 
    public void travel()
    {
        horse.walk();
    }
}

// Run the demo
public class AdapterDemo{
    public static void main(String[] args){
        Passenger p = new Passenger();
        Car c = new Car();
        p.travel(c);

        Horse h = new Horse();
        Transport hTransport = new HorseToTransportAdapter(h);
        hTransport.travel();
    }
}

В результате получаем:

Traveling by car
Riding a horse

Порождающие шаблоны проектирования (Creational)

Порождающие шаблоны проектирования отвечают за создание и инициализацию объектов в соответствии с ситуацией. Они используются для повышения гибкости и повторного использования существующего кода.

Такие шаблоны проектирования позволяют создавать объекты, не заботясь о логике их создания, в отличие от непосредственного создания объектов с помощью оператора new. Это дает программе больше гибкости, позволяя выбирать объекты в соответствии с примером использования.

Порождающие шаблоны можно разделить на шаблоны создания классов и шаблоны создания объектов. Шаблоны создания классов эффективно используют наследование в процессе создания экземпляров, а шаблоны создания объектов для выполнения своей работы эффективно используют делегирование.

Порождающие шаблоны проектирования:

  • фабричный метод (Factory Method);
  • абстрактная фабрика (Abstract Factory);
  • строитель (Builder);
  • одиночка (Singleton);
  • прототип (Prototype).

Рассмотрим пример использования порождающего шаблона проектирования.

Предположим, нужно обеспечить взаимодействие с несколькими серверами баз данных, например MySQL и PostgreSQL. Если в качестве бэкенда используется MySQL, но в будущем планируете перейти на PostgreSQL, то потребуется изменить весь код. Но шаблон проектирования фабрика обеспечит слабое связывание, простую реализацию и создание подобных видов объектов.

Фабричный метод: создание объектов с общим интерфейсом и передача обязанностей по созданию экземпляров подклассам

Используя этот шаблон проектирования, мы создаем объект, не раскрывая логику реализации клиенту, и обращаемся к новому объекту посредством общего интерфейса.

Абстрактная фабрика: создание семейства взаимосвязанных объектов

Шаблон проектирования абстрактная фабрика выступает как сверхфабрика, которая создает другие фабрики. Она также называется фабрикой фабрик.

Интерфейс в этом шаблоне отвечает за создание фабрики связанных объектов без указания их классов. Каждая созданная фабрика может создавать объекты с использованием шаблона фабрика.

Строитель: пошаговый шаблон для создания сложных объектов с отделением конструирования от представления

Шаблон проектирования строитель пошагово создает сложный объект из простых объектов. Такой строитель не зависит от других объектов.

Прототип: копирование существующих объектов, при котором код не становится зависимым от классов

Шаблон прототип относится к созданию дубликата объекта с учетом производительности.

В соответствии с этим шаблоном проектирования создается интерфейс прототипа, который дает указание создать клон текущего объекта. Этот шаблон используется, когда непосредственное создание объекта обходится дорого.

Например, нужно создать объект после требовательной к ресурсам операции над базой данных. Мы можем кэшировать объект, возвратить его клон при следующем запросе и обновить базу данных по мере необходимости, сокращая количество обращений к ней.

Шаблон одиночка: ограничение количества объектов класса одним экземпляром

Шаблон проектирования одиночка — один из самых простых шаблонов проектирования.

Реализация этого шаблона заключается в создании одного класса, который отвечает за создание объекта и гарантирует, что созданный объект будет единственным. К этому объекту можно обращаться напрямую без необходимости создания экземпляра класса.

Структурные шаблоны проектирования (Structural)

Структурные шаблоны проектирования отвечают за объединение классов и объектов в более крупные структуры, которые обеспечивают новую функциональность.

Структурные шаблоны проектирования:

  • адаптер (Adapter);
  • мост (Bridge);
  • компоновщик (Composite);
  • декоратор (Decorator);
  • фасад (Facade);
  • легковес (Flyweight);
  • заместитель (Proxy).

Давайте рассмотрим пример использования структурного шаблона проектирования.

Когда два интерфейса несовместимы друг с другом и требуется обеспечить взаимосвязь между ними, используется шаблон проектирования адаптер. Этот шаблон преобразует интерфейс класса в другой интерфейс или класс, которого ожидает клиент. Он позволяет двум несовместимым классам работать совместно.

Адаптер: адаптация интерфейса к интерфейсу другого существующего класса для обеспечения взаимодействия несовместимых интерфейсов

Шаблон проектирования адаптер выполняет роль моста между двумя несовместимыми интерфейсами. Он относится к структурным, потому что объединяет возможности двух независимых интерфейсов.

Мост: метод отделения интерфейса от реализации

Шаблон проектирования мост используется, когда необходимо отделить абстракцию от ее реализации, чтобы из можно было изменять независимо друг от друга.

В этом шаблоне используется интерфейс, который выступает мостом, который обеспечивает независимость функций конкретных классов от классов реализации интерфейсов. Структурное изменение класса одного типа не затронет класс другого типа.

Компоновщик: группировка объектов в древовидную структуру, которой можно управлять, как одним объектом

Шаблон проектирования компоновщик используется, когда нужно взаимодействовать с группой объектов как с одним. Этот шаблон объединяет объекты в древовидную структуру.

Он создает класс, который содержит группы собственных объектов. Этот класс предоставляет способы изменения группы этих объектов.

Декоратор: динамическое расширение (добавление или переопределение) функций

Шаблон проектирования декоратор позволяет пользователям добавлять новые функции в существующий объект, не изменяя его структуры.

Этот шаблон создает класс декоратора, который является оболочкой существующего класса и добавляет новые функции, не изменяя сигнатуры методов класса.

Фасад: определение высокоуровневого интерфейса для упрощения использования крупного тела кода

Шаблон проектирования фасад скрывает сложность системы и предоставляет клиенту интерфейс, с помощью которого клиент может обращаться к системе.

Этот шаблон подразумевает создание одного класса, который предоставляет упрощенные методы, нужные клиенту, и делегирует вызовы методов существующих классов системы.

Легковес: снижение потребления памяти за счет совместного использования данных с подобными объектами

Шаблон проектирования легковес используется, в основном, для сокращения количества создаваемых объектов, уменьшения нагрузки на память и повышения производительности.

Этот шаблон пытается повторно использовать уже существующие подобные виды объектов, сохраняя их, и создавая новые объекты, когда совпадающих объектов не найдено.

Заместитель: представление объекта другим объектом для контроля доступа и упрощения

При использовании шаблона проектирования заместитель один класс представляет функции другого класса.

При использовании этого шаблона создается объект, содержащий исходный и предоставляющий интерфейс для взаимодействия со внешним миром.

Поведенческие шаблоны проектирования (Behavioral)

Поведенческие шаблоны проектирования отвечают за взаимосвязь между объектами и за распределение обязанностей между ними.

Поведенческие шаблоны проектирования:

  • цепочка обязанностей (Chain of responsibility);
  • команда (Command);
  • итератор (Iterator);
  • посредник (Mediator);
  • снимок (Memento);
  • наблюдатель (Observer);
  • состояние (State);
  • стратегия (Strategy);
  • шаблонный метод (Template method);
  • посетитель (Visitor ).

Пример использования поведенческого шаблона проектирования

К примеру, вы хотите, чтобы поведение создаваемого вами модуля в будущем могло расширяться, чтобы он вел себя по-разному в зависимости от потребностей приложения. Но никому не разрешено изменять исходный код, то есть вы можете только дополнять, а не изменять его структуру.

В таком случае разработчик может применить шаблон проектирования шаблонный метод. Он определяет скелет алгоритма и делегирует некоторые шаги подклассам. Подклассы могут переопределять некоторые шаги алгоритма без изменения его структуры.

Цепочка обязанностей: метод делегирования команд цепочке обрабатывающих объектов

Как следует из названия, шаблон проектирования цепочка обязанностей создает цепочку объектов-получателей для запроса. Этот шаблон отделяет отправителя от получателя запроса в зависимости от типа запроса.

При использовании этого шаблона обычно каждый получатель содержит ссылку на другого получателя. Если один объект не может обработать запрос, и то он передает его следующему получателю и так далее.

Команда: инкапсуляция запроса команды в объекте

Шаблон проектирования команда — это управляемый данными шаблон. Запрос помещается в объект-оболочку как команда и передается вызывающему объекту. Вызывающий объект ищет подходящий объект, который может выполнить эту команду, передает ему эту команду, а последний, в свою очередь, выполняет ее.

Итератор: обеспечение итеративного (последовательного) доступа к элементам коллекции

Шаблон проектирования широко применяется в средах программирования Java и .Net. Он используется для последовательного обращения к объектам коллекции без необходимости знать их базовое представление.

Посредник: обеспечение простоты взаимодействия между классами

Шаблон проектирования посредник используется для упрощения взаимодействия между несколькими объектами или классами. Этот шаблон предоставляет класс-посредник, который обычно обеспечивает все взаимодействие между различными классами и простоту обслуживания кода за счет слабого связывания.

Снимок: процесс сохранения и восстановления исходного состояния объекта

Шаблон проектирования снимок используется для восстановления прежнего состояния объекта.

Наблюдатель: определение способа уведомления объектов об изменениях в других объектах

Шаблон проектирования наблюдатель используется, когда между объектами существует отношение «один ко многим», и если один объект меняется, то зависимые объекты автоматически получают уведомление.

Состояние: изменение поведения объекта при изменении его состояния

При использовании шаблона проектирования «состояние» поведение класса меняется в зависимости от его состояния.

Для его реализации создаются классы, которые представляют собой различные состояния и объект контекста, поведение которого меняется при изменении объекта состояния.

Стратегия: инкапсуляций алгоритма в классе

При использовании шаблона проектирования стратегия поведение класса или его алгоритм можно изменять во время выполнения.

Для реализации этого шаблона создаются объекты, представляющие различные стратегии и объект контекста, поведение которого меняется в зависимости от объекта стратегии. Объект стратегии изменяет алгоритм выполнения объекта контекста.

Посетитель: определение новой операции над классом без изменения класса

В шаблоне проектирования посетитель используется класс-посетитель, который изменяет алгоритм выполнения класса элемента. Таким образом алгоритм выполнения элемента изменяется тогда, когда изменяется посетитель и таким образом каким изменяется этот посетитель.

Шаблонный метод: определение скелета операции и предоставление подклассам права на уточнение некоторых шагов

При использовании шаблона проектирования шаблонный метод некий абстрактный класс предоставляет определенные пути или шаблоны выполнения его методов. Подклассы могут переопределять реализацию метода в зависимости от необходимости, но вызов осуществляется так, как указано абстрактным классом.

Заключение

Шаблоны проектирования программного обеспечения имеют как преимущества, так и недостатки. Важно знать, когда следует применять шаблоны и когда нужно избегать их применения, а также как лучше реализовать определенный шаблон.

В завершение приводим подробную таблицу шаблонов проектирования.

Назначение Шаблон проектирования Аспекты, которые могут изменяться
Порождающие Абстрактная фабрика семейства объектов продуктов
Строитель способ создания сложного объекта
Фабричный метод подкласс объекта, экземпляр которого создается
Прототип класс объекта, экземпляр которого создается
Одиночка единственный экземпляр класса
Структурные Адаптер интерфейс объекта
Мост реализация объекта
Компоновщик структура и состав объекта
Декоратор обязанности объекта без создания подклассов
Фасад интерфейс подсистемы
Легковес затраты на хранение объектов
Заместитель способ доступа к объекту; его расположение
Поведенческие Цепочка обязанностей объект, который может выполнить запрос
Команда время и способ выполнения запроса
Интерпретатор грамматика и интерпретация языка
Итератор способ доступа к элементам целого и их обхода
Mediator подбор объектов для взаимодействия и способ их взаимодейтсвия друг с другом
Снимок частная информация, которая хранится вне объекта, и время ее сохранения
Наблюдатель количество объектов, которые зависят от другого объекта; способ поддержания актуальности зависимых объектов
Состояние состояния объекта
Стратегия алгоритм
Шаблонный метод шаги алгоритма
Посетитель операции, которые можно применить к объектам без изменения  их классов

Полный код примеров можно найти в этом репозитории.

Напоследок ловите курс по шаблонам проектирования:

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023