Ответ
Паттерны проектирования — это типовые, проверенные решения часто встречающихся проблем в проектировании ПО. Они делятся на три основные категории.
1. Порождающие паттерны (Creational)
Управляют процессом создания объектов, делая его более гибким и независимым.
- Singleton (Одиночка): Гарантирует, что у класса существует только один экземпляр, и предоставляет глобальную точку доступа к нему.
public class Logger { // Пример логгера private static volatile Logger instance; private Logger() {} // Приватный конструктор public static Logger getInstance() { if (instance == null) { // Double-Checked Locking для потокобезопасности synchronized (Logger.class) { if (instance == null) { instance = new Logger(); } } } return instance; } public void log(String message) { /* ... */ } } // Использование: Logger.getInstance().log("Message"); - Factory Method (Фабричный метод): Определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта.
- Abstract Factory (Абстрактная фабрика): Создает семейства связанных объектов без указания их конкретных классов.
- Builder (Строитель): Позволяет создавать сложные объекты пошагово, отделяя конструирование от представления. Полезен для объектов со множеством необязательных параметров.
public class Computer { private final String CPU; // обязательный private final String RAM; // обязательный private final String storage; // необязательный private final String graphicsCard; // необязательный // Приватный конструктор, принимающий Builder private Computer(Builder builder) { this.CPU = builder.CPU; this.RAM = builder.RAM; this.storage = builder.storage; this.graphicsCard = builder.graphicsCard; } public static class Builder { private final String CPU; private final String RAM; private String storage; private String graphicsCard; public Builder(String cpu, String ram) { this.CPU = cpu; this.RAM = ram; } public Builder storage(String storage) { this.storage = storage; return this; } public Builder graphicsCard(String gpu) { this.graphicsCard = gpu; return this; } public Computer build() { return new Computer(this); } } } // Использование: Computer pc = new Computer.Builder("Intel i7", "16GB").storage("1TB SSD").build();
2. Структурные паттерны (Structural)
Объединяют классы и объекты в более крупные структуры.
- Adapter (Адаптер): Позволяет объектам с несовместимыми интерфейсами работать вместе. Преобразует интерфейс одного класса в интерфейс, ожидаемый клиентом.
// Старая система public class LegacyPrinter { public void printDocument(String text) { /* печать */ } } // Новый интерфейс, который требуется клиенту public interface ModernPrinter { void print(String content); } // Адаптер public class PrinterAdapter implements ModernPrinter { private LegacyPrinter legacyPrinter; public PrinterAdapter(LegacyPrinter printer) { this.legacyPrinter = printer; } @Override public void print(String content) { legacyPrinter.printDocument(content); // Адаптация вызова } } - Decorator (Декоратор): Динамически добавляет объекту новые обязанности, являясь гибкой альтернативой наследованию для расширения функциональности.
- Proxy (Заместитель): Предоставляет объект-заменитель, который контролирует доступ к другому объекту (ленивая инициализация, кеширование, защита).
- Facade (Фасад): Предоставляет простой интерфейс к сложной подсистеме, скрывая ее детали.
3. Поведенческие паттерны (Behavioral)
Решают задачи эффективного взаимодействия и распределения ответственности между объектами.
- Observer (Наблюдатель): Определяет зависимость "один-ко-многим" между объектами так, что при изменении состояния одного объекта все зависящие от него объекты уведомляются и обновляются автоматически. Основа event-систем.
public interface Observer { void update(String event); } public class ConcreteObserver implements Observer { private String name; @Override public void update(String event) { System.out.println(name + " received: " + event); } } public class Subject { private List<Observer> observers = new ArrayList<>(); public void addObserver(Observer o) { observers.add(o); } public void notifyObservers(String event) { for (Observer o : observers) { o.update(event); } } } - Strategy (Стратегия): Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Позволяет изменять алгоритм независимо от клиента, который его использует.
- Command (Команда): Инкапсулирует запрос как объект, позволяя параметризовать клиентов с различными запросами, ставить запросы в очередь или поддерживать отмену операций.