Паттерны проектирования в Java с примерами: полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Паттерны проектирования в Java: от теории к практике

Паттерны проектирования (Design Patterns) — это проверенные решения типовых задач при разработке программного обеспечения. Для Java-разработчика знание паттернов — не просто преимущество, а необходимость. Они позволяют писать гибкий, поддерживаемый и масштабируемый код. В этой статье мы разберём четыре ключевых паттерна из каталога GoF (Gang of Four) с реальными примерами на Java.

Почему паттерны важны для Java-разработчика?

Java — строго типизированный объектно-ориентированный язык, где паттерны помогают:

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

1. Singleton (Одиночка)

Паттерн Singleton гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему. В Java классическая реализация выглядит так:

public class DatabaseConnection {
    private static volatile DatabaseConnection instance;
    private String connectionString;

    private DatabaseConnection(String connectionString) {
        this.connectionString = connectionString;
    }

    public static DatabaseConnection getInstance(String connectionString) {
        if (instance == null) {
            synchronized (DatabaseConnection.class) {
                if (instance == null) {
                    instance = new DatabaseConnection(connectionString);
                }
            }
        }
        return instance;
    }

    public void connect() {
        System.out.println("Connected to: " + connectionString);
    }
}

Когда использовать Singleton?

  • Управление подключением к базе данных.
  • Логгеры и конфигурационные менеджеры.
  • Пул потоков (Thread Pool).

Важно: В современных Java-приложениях Singleton часто заменяют внедрением зависимостей (DI-контейнеры, Spring).

2. Factory Method (Фабричный метод)

Factory Method определяет интерфейс для создания объекта, но позволяет подклассам решать, какой класс инстанциировать. Рассмотрим пример создания уведомлений:

// Интерфейс продукта
interface Notification {
    void send(String message);
}

// Конкретные продукты
class EmailNotification implements Notification {
    @Override
    public void send(String message) {
        System.out.println("Sending Email: " + message);
    }
}

class SMSNotification implements Notification {
    @Override
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

// Фабрика
abstract class NotificationFactory {
    public abstract Notification createNotification();

    public void notifyUser(String message) {
        Notification notification = createNotification();
        notification.send(message);
    }
}

class EmailNotificationFactory extends NotificationFactory {
    @Override
    public Notification createNotification() {
        return new EmailNotification();
    }
}

class SMSNotificationFactory extends NotificationFactory {
    @Override
    public Notification createNotification() {
        return new SMSNotification();
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        NotificationFactory factory = new EmailNotificationFactory();
        factory.notifyUser("Hello, User!");
    }
}

Преимущества Factory Method:

  • Код не зависит от конкретных классов.
  • Добавление новых типов уведомлений не требует изменения существующего кода (принцип Open/Closed).
  • Упрощает юнит-тестирование через моки.

3. Observer (Наблюдатель)

Паттерн Observer создаёт механизм подписки, позволяющий одним объектам следить за изменениями других. Реализуем простую систему уведомлений о погоде:

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

// Интерфейс наблюдателя
interface Observer {
    void update(float temperature);
}

// Интерфейс субъекта
interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers();
}

// Конкретный субъект
class WeatherStation implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private float temperature;

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature);
        }
    }

    public void setTemperature(float temperature) {
        this.temperature = temperature;
        notifyObservers();
    }
}

// Конкретный наблюдатель
class TemperatureDisplay implements Observer {
    private String name;

    public TemperatureDisplay(String name) {
        this.name = name;
    }

    @Override
    public void update(float temperature) {
        System.out.println(name + " показывает: " + temperature + "°C");
    }
}

// Использование
public class WeatherApp {
    public static void main(String[] args) {
        WeatherStation station = new WeatherStation();
        TemperatureDisplay display1 = new TemperatureDisplay("Дисплей 1");
        TemperatureDisplay display2 = new TemperatureDisplay("Дисплей 2");

        station.attach(display1);
        station.attach(display2);

        station.setTemperature(25.5f);
        station.setTemperature(27.0f);
    }
}

Где применяется Observer в Java?

  • Java Swing/AWT — обработчики событий (ActionListener).
  • Паттерн Model-View-Controller (MVC).
  • Системы подписок в микросервисах (Event Bus).

4. Builder (Строитель)

Builder отделяет конструирование сложного объекта от его представления. Особенно полезен, когда у класса много опциональных параметров. Пример — создание объекта Pizza:

public class Pizza {
    private String dough;
    private String sauce;
    private boolean cheese;
    private boolean pepperoni;
    private boolean mushrooms;

    private Pizza(PizzaBuilder builder) {
        this.dough = builder.dough;
        this.sauce = builder.sauce;
        this.cheese = builder.cheese;
        this.pepperoni = builder.pepperoni;
        this.mushrooms = builder.mushrooms;
    }

    // Геттеры (опущены для краткости)

    public static class PizzaBuilder {
        private String dough;
        private String sauce;
        private boolean cheese = false;
        private boolean pepperoni = false;
        private boolean mushrooms = false;

        public PizzaBuilder(String dough, String sauce) {
            this.dough = dough;
            this.sauce = sauce;
        }

        public PizzaBuilder addCheese() {
            this.cheese = true;
            return this;
        }

        public PizzaBuilder addPepperoni() {
            this.pepperoni = true;
            return this;
        }

        public PizzaBuilder addMushrooms() {
            this.mushrooms = true;
            return this;
        }

        public Pizza build() {
            return new Pizza(this);
        }
    }
}

// Использование
public class PizzaShop {
    public static void main(String[] args) {
        Pizza pizza = new Pizza.PizzaBuilder("Тонкое", "Томатный")
                .addChe                

Блоги

Рекомендаций по книгам