October 7

Готовые решения для гениального кода: Путеводитель по паттернам проектирования GoF на примере Java

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

Паттерны проектирования — это повторно используемые решения типичных проблем проектирования, встречающихся в программировании. Авторы — Эрих Гамма, Ричард Хелм, Ральф Джонсон и Джон Влиссидес (Gang of Four) — представили систематизированный подход к проектированию программного обеспечения, ставший эталонным для поколения разработчиков.

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

Какие существуют паттерны GoF и зачем они нужны?

Паттерны GoF традиционно делятся на три категории:

1. Creational (создание объектов)
✅ Решают вопросы инициализации и создания объектов.

Singleton (Одиночка)
Гарантирует существование ровно одного экземпляра класса и предоставляет глобальную точку доступа к нему. Пример реализации на Java:

public final class Logger {
    private static volatile Logger instance = null;
    
    // Private constructor prevents instantiation from outside
    private Logger() {}
    public static Logger getInstance() {
        if (instance == null) { // Double-check locking pattern
            synchronized (Logger.class) {
                if (instance == null) {
                    instance = new Logger();
                }
            }
        }
        return instance;
    }
}

Factory Method (Фабричный метод)
Делегирует создание объектов подклассам. Пример:

abstract class Button {
    abstract void render();
}

class WinButton extends Button {
    @Override
    void render() {
        System.out.println("Rendering Windows button");
    }
}

class MacButton extends Button {
    @Override
    void render() {
        System.out.println("Rendering macOS button");
    }
}

// Client uses factory method to create buttons
class Dialog {
    protected abstract Button createButton();
    public void renderDialog() {
        Button btn = this.createButton(); // Factory method invocation
        btn.render();
    }
}

class WinDialog extends Dialog {
    @Override
    protected Button createButton() {
        return new WinButton();
    }
}

class MacDialog extends Dialog {
    @Override
    protected Button createButton() {
        return new MacButton();
    }
}

2. Structural (структурная организация)
✅ Организует связи между объектами и упрощает их интеграцию и модификацию.

Adapter (Адаптер)
Преобразует интерфейс одного класса в требуемый клиентами. Пример:

interface Printable {
    void print();
}

class TextDocument implements Printable {
    @Override
    public void print() {
        System.out.println("Printing document...");
    }
}

// Legacy system has incompatible interface
class OldPrinter {
    public void legacyPrint(Object obj) {
        System.out.println("Legacy printing mechanism...");
    }
}

// Adapter bridges old printer with modern documents
class PrinterAdapter implements Printable {
    private OldPrinter printer;
    
    public PrinterAdapter(OldPrinter p) {
        this.printer = p;
    }
    
    @Override
    public void print() {
        printer.legacyPrint(this);
    }
}

Facade (Фасад)
Упрощает сложный интерфейс подсистемы. Пример:

class Computer {
    boolean cpuRunning = false;
    boolean hddSpinning = false;

    void startCPU() {
        cpuRunning = true;
    }
    
    void spinHDD() {
        hddSpinning = true;
    }
    
    void halt() {
        cpuRunning = false;
        hddSpinning = false;
    }
}

// Facade simplifies starting/shutting down process
class ComputerFacade {
    private Computer comp;
    
    public ComputerFacade(Computer c) {
        this.comp = c;
    }
    
    public void turnOn() {
        comp.startCPU();
        comp.spinHDD();
    }
    
    public void shutDown() {
        comp.halt();
    }
}

3. Behavioral (поведенческие модели)
✅ Управляют взаимодействием между объектами и координацией действий.

Observer (Наблюдатель)
Оповещает наблюдателей при изменении состояния. Пример:

interface Observer {
    void update(String message);
}

class Subject {
    List<Observer> observers = new ArrayList<>();
    String state;

    public void attach(Observer observer) {
        observers.add(observer);
    }
    
    public void detach(Observer observer) {
        observers.remove(observer);
    }
    
    public void notifyObservers() {
        for (Observer o : observers)
            o.update(state);
    }

    public void setState(String s) {
        this.state = s;
        notifyObservers();
    }
}

class ConcreteObserver implements Observer {
    @Override
    public void update(String msg) {
        System.out.println("Received update: " + msg);
    }
}

Command (Команда)
Инкапсулирует запрос в объект, обеспечивая возможность параметризовать действия. Пример:

interface Command {
    void execute();
}

class Light {
    public void switchOn() {
        System.out.println("Light turned ON!");
    }
    
    public void switchOff() {
        System.out.println("Light turned OFF!");
    }
}

class TurnOnCommand implements Command {
    private Light light;
    
    public TurnOnCommand(Light l) {
        this.light = l;
    }
    
    @Override
    public void execute() {
        light.switchOn();
    }
}

class TurnOffCommand implements Command {
    private Light light;
    
    public TurnOffCommand(Light l) {
        this.light = l;
    }
    
    @Override
    public void execute() {
        light.switchOff();
    }
}

class RemoteController {
    private Command command;
    
    public void setCommand(Command cmd) {
        this.command = cmd;
    }
    
    public void pressButton() {
        command.execute();
    }
}

Заключение

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