Skip to content
IRC-CodingIRC-Coding
Design PatternsCreational PatternsSingleton FactoryBuilder PrototypeAbstract FactoryПрограммирование

Design Patterns: Creational - Singleton, Factory, Builder

Creational Design Patterns: Singleton, Factory, Builder, Prototype, Abstract Factory. Практические примеры на Java.

S

schutzgeist

17 min read
Design Patterns: Creational - Singleton, Factory, Builder

Design Patterns: Порождающие паттерны - Singleton, Factory, Builder, Prototype и Abstract Factory

Другие статьи о Design Patterns:

Это руководство охватывает Creational Design Patterns полностью: Singleton, Factory, Builder, Prototype и Abstract Factory с практическими примерами.

Суть в двух словах

Creational Patterns абстрагируют создание объектов. Singleton гарантирует одну инстанцию, Factory инкапсулирует логику создания, Builder позволяет пошаговую конструкцию, Prototype клонирует объекты, Abstract Factory создаёт семейства связанных объектов.

Краткое определение

Creational Design Patterns это порождающие паттерны, которые управляют процессом инстанцирования объектов и делают его более гибким.

Ключевые Creational Patterns:

Singleton Pattern

  • Назначение: гарантирует единственную инстанцию
  • Применение: логирование, конфигурация, пулы потоков
  • Реализация: приватный конструктор, статическая инстанция
  • Потокобезопасность: synchronized или eager initialization

Factory Method Pattern

  • Назначение: делегирует создание объектов подклассам
  • Применение: фреймворки, системы плагинов
  • Реализация: абстрактный Factory метод
  • Преимущество: слабая связанность, расширяемость

Abstract Factory Pattern

  • Назначение: создание семейств связанных объектов
  • Применение: UI-наборы инструментов, драйверы БД
  • Реализация: интерфейс Factory с конкретными реализациями
  • Преимущество: согласованные семейства объектов

Builder Pattern

  • Назначение: пошаговое построение сложных объектов
  • Применение: объекты конфигурации, DSL
  • Реализация: Fluent Interface, отдельный класс Builder
  • Преимущество: читаемость, гибкость

Prototype Pattern

  • Назначение: создание объектов через клонирование
  • Применение: оптимизированное создание в терминах производительности
  • Реализация: интерфейс Cloneable, метод clone()
  • Преимущество: экономия при дорогостоящем создании

Ключевые моменты для экзамена

  • Creational Patterns: порождающие паттерны для гибкого инстанцирования
  • Singleton: только одна инстанция, глобальные точки доступа
  • Factory Method: создание объектов через подклассы
  • Abstract Factory: создание семейств объектов
  • Builder: пошаговое построение сложных объектов
  • Prototype: создание объектов через клонирование
  • Релевантно для IHK: важно для гибкой архитектуры

Основные компоненты

  1. Singleton: глобальная инстанция с управляемым доступом
  2. Factory Method: абстрагированное создание объектов
  3. Abstract Factory: интерфейс для семейств объектов
  4. Builder: Fluent Interface для сложной конструкции
  5. Prototype: создание на основе клонирования
  6. Dependency Injection: инверсия управления
  7. Factory Configuration: создание на основе конфигурации
  8. Object Pool: переиспользование объектов

Практические примеры

1. Singleton Pattern

// Eager Initialization (потокобезопасно)
public class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    private EagerSingleton() {
        // Приватный конструктор предотвращает инстанцирование
    }
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
    
    public void doSomething() {
        System.out.println("Eager Singleton работает");
    }
}

// Lazy Initialization с Double-Checked Locking
public class LazySingleton {
    private static volatile LazySingleton instance;
    
    private LazySingleton() {
        // Приватный конструктор
    }
    
    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
    
    public void performAction() {
        System.out.println("Lazy Singleton в действии");
    }
}

// Singleton с Enum (потокобезопасно, safe for serialization)
public enum EnumSingleton {
    INSTANCE;
    
    public void doWork() {
        System.out.println("Enum Singleton работает");
    }
}

// Logger как Singleton
public class Logger {
    private static volatile Logger instance;
    private final List<String> logs = new ArrayList<>();
    
    private Logger() {}
    
    public static Logger getInstance() {
        if (instance == null) {
            synchronized (Logger.class) {
                if (instance == null) {
                    instance = new Logger();
                }
            }
        }
        return instance;
    }
    
    public void log(String message) {
        String timestamp = new Date().toString();
        String logEntry = "[" + timestamp + "] " + message;
        logs.add(logEntry);
        System.out.println(logEntry);
    }
    
    public List<String> getLogs() {
        return new ArrayList<>(logs);
    }
    
    public void clearLogs() {
        logs.clear();
    }
}

// Демонстрация Singleton
public class SingletonDemo {
    public static void main(String[] args) {
        System.out.println("=== Singleton Pattern Demo ===");
        
        // Eager Singleton
        EagerSingleton eager1 = EagerSingleton.getInstance();
        EagerSingleton eager2 = EagerSingleton.getInstance();
        
        System.out.println("Eager Singleton - одна инстанция: " + (eager1 == eager2));
        eager1.doSomething();
        
        // Lazy Singleton
        LazySingleton lazy1 = LazySingleton.getInstance();
        LazySingleton lazy2 = LazySingleton.getInstance();
        
        System.out.println("Lazy Singleton - одна инстанция: " + (lazy1 == lazy2));
        lazy2.performAction();
        
        // Enum Singleton
        EnumSingleton enum1 = EnumSingleton.INSTANCE;
        EnumSingleton enum2 = EnumSingleton.INSTANCE;
        
        System.out.println("Enum Singleton - одна инстанция: " + (enum1 == enum2));
        enum1.doWork();
        
        // Logger Singleton
        Logger logger1 = Logger.getInstance();
        Logger logger2 = Logger.getInstance();
        
        System.out.println("Logger Singleton - одна инстанция: " + (logger1 == logger2));
        
        logger1.log("Приложение запущено");
        logger2.log("Пользователь вошёл");
        
        System.out.println("Логи: " + logger1.getLogs());
    }
}

2. Factory Method Pattern

// Абстрактный класс продукта
abstract class Fahrzeug {
    protected String marke;
    protected String modell;
    
    public Fahrzeug(String marke, String modell) {
        this.marke = marke;
        this.modell = modell;
    }
    
    public abstract void starten();
    public abstract void bremsen();
    
    @Override
    public String toString() {
        return marke + " " + modell;
    }
}

// Конкретные продукты
class Auto extends Fahrzeug {
    public Auto(String marke, String modell) {
        super(marke, modell);
    }
    
    @Override
    public void starten() {
        System.out.println("Auto " + this + " запущено");
    }
    
    @Override
    public void bremsen() {
        System.out.println("Auto " + this + " торможит");
    }
}

class Motorrad extends Fahrzeug {
    public Motorrad(String marke, String modell) {
        super(marke, modell);
    }
    
    @Override
    public void starten() {
        System.out.println("Motorrad " + this + " запущено");
    }
    
    @Override
    public void bremsen() {
        System.out.println("Motorrad " + this + " торможит");
    }
}

// Абстрактный класс Factory
abstract class FahrzeugFabrik {
    // Factory Method
    public abstract Fahrzeug erstelleFahrzeug(String marke, String modell);
    
    // Template Method
    public void produziereUndTeste(String marke, String modell) {
        Fahrzeug fahrzeug = erstelleFahrzeug(marke, modell);
        System.out.println("Произведено: " + fahrzeug);
        
        // Проверка качества
        fahrzeug.starten();
        fahrzeug.bremsen();
        System.out.println("Проверка пройдена");
    }
}

// Конкретные Factory
class AutoFabrik extends FahrzeugFabrik {
    @Override
    public Fahrzeug erstelleFahrzeug(String marke, String modell) {
        return new Auto(marke, modell);
    }
}

class MotorradFabrik extends FahrzeugFabrik {
    @Override
    public Fahrzeug erstelleFahrzeug(String marke, String modell) {
        return new Motorrad(marke, modell);
    }
}

// Factory с параметрами
class FahrzeugFactory {
    public static Fahrzeug erstelleFahrzeug(String typ, String marke, String modell) {
        switch (typ.toLowerCase()) {
            case "auto":
                return new Auto(marke, modell);
            case "motorrad":
                return new Motorrad(marke, modell);
            default:
                throw new IllegalArgumentException("Неизвестный тип: " + typ);
        }
    }
}

// Демонстрация Factory Method
public class FactoryMethodDemo {
    public static void main(String[] args) {
        System.out.println("=== Factory Method Pattern Demo ===");
        
        // Factory Method Pattern
        FahrzeugFabrik autoFabrik = new AutoFabrik();
        FahrzeugFabrik motorradFabrik = new MotorradFabrik();
        
        System.out.println("--- Производство Auto ---");
        autoFabrik.produziereUndTeste("VW", "Golf");
        
        System.out.println("\n--- Производство Motorrad ---");
        motorradFabrik.produziereUndTeste("BMW", "R1200");
        
        // Static Factory
        System.out.println("\n--- Static Factory ---");
        Fahrzeug vwGolf = FahrzeugFactory.erstelleFahrzeug("auto", "VW", "Golf");
        Fahrzeug bmwR1200 = FahrzeugFactory.erstelleFahrzeug("motorrad", "BMW", "R1200");
        
        vwGolf.starten();
        bmwR1200.starten();
        
        // Factory с конфигурацией
        System.out.println("\n--- Factory с конфигурацией ---");
        FahrzeugKonfigurator konfigurator = new FahrzeugKonfigurator();
        
        Fahrzeug auto1 = konfigurator.erstelleFahrzeug("auto", "Mercedes", "C-Klasse");
        Fahrzeug motorrad1 = konfigurator.erstelleFahrzeug("motorrad", "Harley", "Sportster");
        
        auto1.starten();
        motorrad1.starten();
    }
}

// Factory с конфигурацией
class FahrzeugKonfigurator {
    private Map<String, FahrzeugFabrik> fabriken = new HashMap<>();
    
    public FahrzeugKonfigurator() {
        fabriken.put("auto", new AutoFabrik());
        fabriken.put("motorrad", new MotorradFabrik());
    }
    
    public Fahrzeug erstelleFahrzeug(String typ, String marke, String modell) {
        FahrzeugFabrik fabrik = fabriken.get(typ.toLowerCase());
        if (fabrik == null) {
            throw new IllegalArgumentException("Неизвестный тип: " + typ);
        }
        return fabrik.erstelleFahrzeug(marke, modell);
    }
    
    public void registriereFabrik(String typ, FahrzeugFabrik fabrik) {
        fabriken.put(typ.toLowerCase(), fabrik);
    }
}

3. Abstract Factory Pattern

// Abstrakte Produkte für UI-Theme
interface Button {
    void render();
    void onClick(Runnable action);
}

interface TextField {
    void render();
    void setText(String text);
    String getText();
}

interface Checkbox {
    void render();
    void setChecked(boolean checked);
    boolean isChecked();
}

// Konkrete Produkte - Windows Theme
class WindowsButton implements Button {
    @Override
    public void render() {
        System.out.println("[Windows Button gerendert]");
    }
    
    @Override
    public void onClick(Runnable action) {
        System.out.println("Windows Button geklickt");
        action.run();
    }
}

class WindowsTextField implements TextField {
    private String text = "";
    
    @Override
    public void render() {
        System.out.println("[Windows TextField: '" + text + "']");
    }
    
    @Override
    public void setText(String text) {
        this.text = text;
    }
    
    @Override
    public String getText() {
        return text;
    }
}

class WindowsCheckbox implements Checkbox {
    private boolean checked = false;
    
    @Override
    public void render() {
        System.out.println("[Windows Checkbox: " + (checked ? "X" : " ") + "]");
    }
    
    @Override
    public void setChecked(boolean checked) {
        this.checked = checked;
    }
    
    @Override
    public boolean isChecked() {
        return checked;
    }
}

// Konkrete Produkte - macOS Theme
class MacButton implements Button {
    @Override
    public void render() {
        System.out.println("[macOS Button gerendert]");
    }
    
    @Override
    public void onClick(Runnable action) {
        System.out.println("macOS Button geklickt");
        action.run();
    }
}

class MacTextField implements TextField {
    private String text = "";
    
    @Override
    public void render() {
        System.out.println("[macOS TextField: '" + text + "']");
    }
    
    @Override
    public void setText(String text) {
        this.text = text;
    }
    
    @Override
    public String getText() {
        return text;
    }
}

class MacCheckbox implements Checkbox {
    private boolean checked = false;
    
    @Override
    public void render() {
        System.out.println("[macOS Checkbox: " + (checked ? "✓" : " ") + "]");
    }
    
    @Override
    public void setChecked(boolean checked) {
        this.checked = checked;
    }
    
    @Override
    public boolean isChecked() {
        return checked;
    }
}

// Abstrakte Factory
interface GUIFactory {
    Button createButton();
    TextField createTextField();
    Checkbox createCheckbox();
}

// Konkrete Factories
class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }
    
    @Override
    public TextField createTextField() {
        return new WindowsTextField();
    }
    
    @Override
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

class MacFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new MacButton();
    }
    
    @Override
    public TextField createTextField() {
        return new MacTextField();
    }
    
    @Override
    public Checkbox createCheckbox() {
        return new MacCheckbox();
    }
}

// Client Code
class Anwendung {
    private Button button;
    private TextField textField;
    private Checkbox checkbox;
    
    public Anwendung(GUIFactory factory) {
        button = factory.createButton();
        textField = factory.createTextField();
        checkbox = factory.createCheckbox();
    }
    
    public void erstelleUI() {
        System.out.println("=== UI wird erstellt ===");
        
        button.render();
        textField.render();
        checkbox.render();
        
        // Funktionalität hinzufügen
        button.onClick(() -> {
            String text = textField.getText();
            System.out.println("Button geklickt mit Text: " + text);
            checkbox.setChecked(!checkbox.isChecked());
            checkbox.render();
        });
        
        textField.setText("Beispieltext");
        textField.render();
    }
}

// Abstract Factory Demo
public class AbstractFactoryDemo {
    public static void main(String[] args) {
        System.out.println("=== Abstract Factory Pattern Demo ===");
        
        // Windows Anwendung
        System.out.println("\n--- Windows Anwendung ---");
        GUIFactory windowsFactory = new WindowsFactory();
        Anwendung windowsApp = new Anwendung(windowsFactory);
        windowsApp.erstelleUI();
        
        // macOS Anwendung
        System.out.println("\n--- macOS Anwendung ---");
        GUIFactory macFactory = new MacFactory();
        Anwendung macApp = new Anwendung(macFactory);
        macApp.erstelleUI();
        
        // Factory basierend auf Betriebssystem
        System.out.println("\n--- Dynamische Factory ---");
        String os = System.getProperty("os.name").toLowerCase();
        GUIFactory factory;
        
        if (os.contains("mac")) {
            factory = new MacFactory();
        } else {
            factory = new WindowsFactory();
        }
        
        Anwendung dynamischeApp = new Anwendung(factory);
        dynamischeApp.erstelleUI();
    }
}

Паттерн Abstract Factory позволяет создавать семейства связанных объектов, не указывая их конкретные классы. В примере выше мы создаём фабрики для разных операционных систем, которые производят соответствующие UI компоненты. Клиентский код работает исключительно с интерфейсами и не знает о конкретных реализациях.

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

4. Builder Pattern

// Komplexes Produkt
class Computer {
    private final String cpu;
    private final String gpu;
    private final int ram;
    private final int storage;
    private final boolean hasWifi;
    private final boolean hasBluetooth;
    private final String operatingSystem;
    
    // Private Konstruktor
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.gpu = builder.gpu;
        this.ram = builder.ram;
        this.storage = builder.storage;
        this.hasWifi = builder.hasWifi;
        this.hasBluetooth = builder.hasBluetooth;
        this.operatingSystem = builder.operatingSystem;
    }
    
    // Getters
    public String getCpu() { return cpu; }
    public String getGpu() { return gpu; }
    public int getRam() { return ram; }
    public int getStorage() { return storage; }
    public boolean hasWifi() { return hasWifi; }
    public boolean hasBluetooth() { return hasBluetooth; }
    public String getOperatingSystem() { return operatingSystem; }
    
    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", gpu='" + gpu + '\'' +
                ", ram=" + ram + "GB" +
                ", storage=" + storage + "GB" +
                ", hasWifi=" + hasWifi +
                ", hasBluetooth=" + hasBluetooth +
                ", operatingSystem='" + operatingSystem + '\'' +
                '}';
    }
    
    // Builder Klasse
    public static class Builder {
        // Erforderliche Parameter
        private final String cpu;
        private final String gpu;
        
        // Optionale Parameter mit Default-Werten
        private int ram = 8;
        private int storage = 256;
        private boolean hasWifi = true;
        private boolean hasBluetooth = false;
        private String operatingSystem = "Windows";
        
        // Constructor für erforderliche Parameter
        public Builder(String cpu, String gpu) {
            this.cpu = cpu;
            this.gpu = gpu;
        }
        
        // Fluent Interface für optionale Parameter
        public Builder ram(int ram) {
            this.ram = ram;
            return this;
        }
        
        public Builder storage(int storage) {
            this.storage = storage;
            return this;
        }
        
        public Builder wifi(boolean hasWifi) {
            this.hasWifi = hasWifi;
            return this;
        }
        
        public Builder bluetooth(boolean hasBluetooth) {
            this.hasBluetooth = hasBluetooth;
            return this;
        }
        
        public Builder operatingSystem(String operatingSystem) {
            this.operatingSystem = operatingSystem;
            return this;
        }
        
        // Build-Methode
        public Computer build() {
            // Validierung
            if (ram < 4) {
                throw new IllegalArgumentException("RAM muss mindestens 4GB sein");
            }
            if (storage < 128) {
                throw new IllegalArgumentException("Storage muss mindestens 128GB sein");
            }
            
            return new Computer(this);
        }
    }
}

// Builder für Konfigurationsobjekte
class Konfiguration {
    private final Map<String, String> eigenschaften;
    private final List<String> aktivierteModule;
    private final String datenbankVerbindung;
    private final int timeout;
    
    private Konfiguration(Builder builder) {
        this.eigenschaften = new HashMap<>(builder.eigenschaften);
        this.aktivierteModule = new ArrayList<>(builder.aktivierteModule);
        this.datenbankVerbindung = builder.datenbankVerbindung;
        this.timeout = builder.timeout;
    }
    
    public static class Builder {
        private Map<String, String> eigenschaften = new HashMap<>();
        private List<String> aktivierteModule = new ArrayList<>();
        private String datenbankVerbindung = "localhost:5432";
        private int timeout = 30;
        
        public Builder eigenschaft(String key, String value) {
            eigenschaften.put(key, value);
            return this;
        }
        
        public Builder modul(String modul) {
            aktivierteModule.add(modul);
            return this;
        }
        
        public Builder datenbankVerbindung(String verbindung) {
            this.datenbankVerbindung = verbindung;
            return this;
        }
        
        public Builder timeout(int sekunden) {
            this.timeout = sekunden;
            return this;
        }
        
        public Konfiguration build() {
            return new Konfiguration(this);
        }
    }
    
    // Getters und toString
    public Map<String, String> getEigenschaften() { return new HashMap<>(eigenschaften); }
    public List<String> getAktivierteModule() { return new ArrayList<>(aktivierteModule); }
    public String getDatenbankVerbindung() { return datenbankVerbindung; }
    public int getTimeout() { return timeout; }
    
    @Override
    public String toString() {
        return "Konfiguration{" +
                "eigenschaften=" + eigenschaften +
                ", aktivierteModule=" + aktivierteModule +
                ", datenbankVerbindung='" + datenbankVerbindung + '\'' +
                ", timeout=" + timeout +
                '}';
    }
}

// Builder Pattern Demo
public class BuilderDemo {
    public static void main(String[] args) {
        System.out.println("=== Builder Pattern Demo ===");
        
        // Einfacher Computer mit Builder
        Computer gamingPC = new Computer.Builder("Intel i9", "NVIDIA RTX 3080")
            .ram(32)
            .storage(1000)
            .wifi(true)
            .bluetooth(true)
            .operatingSystem("Windows 11")
            .build();
        
        System.out.println("Gaming PC: " + gamingPC);
        
        // Minimaler Computer
        Computer officePC = new Computer.Builder("Intel i5", "Intel HD Graphics")
            .build();
        
        System.out.println("Office PC: " + officePC);
        
        // Konfiguration mit Builder
        Konfiguration konfig = new Konfiguration.Builder()
            .eigenschaft("app.name", "MyApp")
            .eigenschaft("app.version", "1.0.0")
            .modul("authentication")
            .modul("logging")
            .modul("database")
            .datenbankVerbindung("prod.db.server.com:5432")
            .timeout(60)
            .build();
        
        System.out.println("\nKonfiguration: " + konfig);
        
        // Nested Builder
        System.out.println("\n--- Nested Builder ---");
        Computer server = new Computer.Builder("AMD EPYC", "AMD Radeon VII")
            .ram(128)
            .storage(4000)
            .wifi(false)
            .operatingSystem("Ubuntu Server")
            .build();
        
        System.out.println("Server: " + server);
        
        // Builder mit Validierung
        try {
            Computer invalidPC = new Computer.Builder("Intel i3", "Intel HD")
                .ram(2)  // Ungültig
                .build();
        } catch (IllegalArgumentException e) {
            System.out.println("Validierungsfehler: " + e.getMessage());
        }
    }
}

Builder Pattern идеален для работы со сложными объектами, которые имеют много параметров, особенно когда многие из них опциональны. Вместо создания множества перегруженных конструкторов (телефонная проблема), мы используем Builder с fluent interface, позволяющий устанавливать параметры по одному.

В примере выше компьютер конфигурируется пошагово, каждый метод возвращает Builder, позволяя продолжить цепь вызовов. Default значения позволяют создавать объекты с минимальной конфигурацией, а валидация в методе build() гарантирует, что объект будет корректным.

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

5. Prototype Pattern

// Prototype Interface
interface Prototype<T> {
    T clone();
}

// Konkrete Prototypen
class Dokument implements Prototype<Dokument> {
    private String titel;
    private String inhalt;
    private List<String> tags;
    private Date erstellDatum;
    
    public Dokument(String titel, String inhalt) {
        this.titel = titel;
        this.inhalt = inhalt;
        this.tags = new ArrayList<>();
        this.erstellDatum = new Date();
    }
    
    // Copy Constructor
    private Dokument(Dokument original) {
        this.titel = original.titel;
        this.inhalt = original.inhalt;
        this.tags = new ArrayList<>(original.tags);
        this.erstellDatum = new Date(original.erstellDatum.getTime());
    }
    
    @Override
    public Dokument clone() {
        return new Dokument(this);
    }
    
    // Getter und Setter
    public String getTitel() { return titel; }
    public void setTitel(String titel) { this.titel = titel; }
    
    public String getInhalt() { return inhalt; }
    public void setInhalt(String inhalt) { this.inhalt = inhalt; }
    
    public void addTag(String tag) { tags.add(tag); }
    public List<String> getTags() { return new ArrayList<>(tags); }
    
    @Override
    public String toString() {
        return "Dokument{" +
                "titel='" + titel + '\'' +
                ", inhalt='" + inhalt + '\'' +
                ", tags=" + tags +
                ", erstellDatum=" + erstellDatum +
                '}';
    }
}

// Grafisches Objekt als Prototype
class GrafikObjekt implements Prototype<GrafikObjekt> {
    private String typ;
    private int x, y;
    private int breite, hoehe;
    private String farbe;
    private List<GrafikObjekt> kinder;
    
    public GrafikObjekt(String typ, int x, int y, int breite, int hoehe, String farbe) {
        this.typ = typ;
        this.x = x;
        this.y = y;
        this.breite = breite;
        this.hoehe = hoehe;
        this.farbe = farbe;
        this.kinder = new ArrayList<>();
    }
    
    // Deep Clone mit Copy Constructor
    private GrafikObjekt(GrafikObjekt original) {
        this.typ = original.typ;
        this.x = original.x;
        this.y = original.y;
        this.breite = original.breite;
        this.hoehe = original.hoehe;
        this.farbe = original.farbe;
        this.kinder = new ArrayList<>();
        
        // Deep clone für Kinder
        for (GrafikObjekt kind : original.kinder) {
            this.kinder.add(kind.clone());
        }
    }
    
    @Override
    public GrafikObjekt clone() {
        return new GrafikObjekt(this);
    }
    
    public void addKind(GrafikObjekt kind) {
        kinder.add(kind);
    }
    
    public void verschieben(int deltaX, int deltaY) {
        this.x += deltaX;
        this.y += deltaY;
        // Kinder ebenfalls verschieben
        for (GrafikObjekt kind : kinder) {
            kind.verschieben(deltaX, deltaY);
        }
    }
    
    @Override
    public String toString() {
        return "GrafikObjekt{" +
                "typ='" + typ + '\'' +
                ", x=" + x + ", y=" + y +
                ", groesse=" + breite + "x" + hoehe +
                ", farbe='" + farbe + '\'' +
                ", kinder=" + kinder.size() +
                '}';
    }
}

// Prototype Registry
class PrototypeRegistry {
    private Map<String, Prototype<?>> prototypen = new HashMap<>();
    
    public void registrierePrototyp(String key, Prototype<?> prototyp) {
        prototypen.put(key, prototyp);
    }
    
    @SuppressWarnings("unchecked")
    public <T extends Prototype<T>> T klonePrototyp(String key) {
        Prototype<?> prototyp = prototypen.get(key);
        if (prototyp == null) {
            throw new IllegalArgumentException("Prototyp nicht gefunden: " + key);
        }
        return (T) prototyp.clone();
    }
    
    public Collection<String> getVerfuegbarePrototypen() {
        return prototypen.keySet();
    }
}

// Prototype Manager für Performance-Optimierung
class PerformancePrototypManager {
    private Map<Class<?>, Prototype<?>> cache = new HashMap<>();
    
    @SuppressWarnings("unchecked")
    public <T extends Prototype<T>> T getOptimiertesClone(Class<T> klasse, T prototyp) {
        if (!cache.containsKey(klasse)) {
            cache.put(klasse, prototyp);
        }
        
        T cached = (T) cache.get(klasse);
        return cached.clone();
    }
    
    public void clearCache() {
        cache.clear();
    }
    
    public int getCacheSize() {
        return cache.size();
    }
}

// Prototype Pattern Demo
public class PrototypeDemo {
    public static void main(String[] args) {
        System.out.println("=== Prototype Pattern Demo ===");
        
        // Einfaches Klonen
        System.out.println("\n--- Einfaches Klonen ---");
        Dokument original = new Dokument("Bericht", "Dies ist ein Testbericht");
        original.addTag("wichtig");
        original.addTag("intern");
        
        System.out.println("Original: " + original);
        
        Dokument kopie = original.clone();
        kopie.setTitel("Bericht - Kopie");
        kopie.addTag("kopie");
        
        System.out.println("Kopie: " + kopie);
        System.out.println("Original nach Kopie: " + original);
        
        // Deep Clone mit komplexen Objekten
        System.out.println("\n--- Deep Clone ---");
        GrafikObjekt container = new GrafikObjekt("Container", 0, 0, 800, 600, "weiß");
        
        GrafikObjekt button1 = new GrafikObjekt("Button", 10, 10, 100, 30, "blau");
        GrafikObjekt button2 = new GrafikObjekt("Button", 120, 10, 100, 30, "grün");
        
        container.addKind(button1);
        container.addKind(button2);
        
        System.out.println("Original Container: " + container);
        
        GrafikObjekt geklonteContainer = container.clone();
        geklonteContainer.verschieben(50, 50);
        
        System.out.println("Geklonter Container: " + geklonteContainer);
        System.out.println("Original nach Verschiebung: " + container);
        
        // Prototype Registry
        System.out.println("\n--- Prototype Registry ---");
        PrototypeRegistry registry = new PrototypeRegistry();
        
        // Prototypen registrieren
        registry.registrierePrototyp("bericht", new Dokument("Standard-Bericht", ""));
        registry.registrierePrototyp("button", new GrafikObjekt("Button", 0, 0, 80, 25, "grau"));
        
        // Prototypen aus Registry klonen
        Dokument neuerBericht = registry.klonePrototyp("bericht");
        neuerBericht.setTitel("Monatsbericht");
        neuerBericht.setInhalt("Monatliche Statistiken...");
        
        GrafikObjekt neuerButton = registry.klonePrototyp("button");
        neuerButton.verschieben(200, 100);
        
        System.out.println("Neuer Bericht: " + neuerBericht);
        System.out.println("Neuer Button: " + neuerButton);
        
        System.out.println("Verfügbare Prototypen: " + registry.getVerfuegbarePrototypen());
        
        // Performance-Optimierung
        System.out.println("\n--- Performance-Optimierung ---");
        PerformancePrototypManager manager = new PerformancePrototypManager();
        
        Dokument template = new Dokument("Template", "Vorlageninhalt");
        
        // Viele Klone mit Optimierung
        long startZeit = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            Dokument schnell = manager.getOptimiertesClone(Dokument.class, template);
            schnell.setTitel("Dokument " + i);
        }
        long endZeit = System.currentTimeMillis();
        
        System.out.println("1000 optimierte Klone: " + (endZeit - startZeit) + "ms");
        System.out.println("Cache Größe: " + manager.getCacheSize());
    }
}

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

В примере выше класс Dokument реализует интерфейс Prototype<T>. Он содержит copy constructor, который создаёт глубокую копию объекта, включая все поля и вложенные объекты. Метод clone() использует этот конструктор для создания независимой копии.

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

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

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

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

Обзор порождающих паттернов

ПаттернНазначениеКогда использоватьПреимущества
SingletonГарантировать единственный экземплярЛоггеры, конфигурацияГлобальный доступ, экономия памяти
Factory MethodДелегировать создание объектовФреймворки, плагиныСлабая связанность, расширяемость
Abstract FactoryСоздавать семейства объектовUI-наборы инструментов, базы данныхСогласованные объекты
BuilderПошагово конструировать сложные объектыКонфигурация, DSLЧитаемость, гибкость
PrototypeСоздавать объекты через клонированиеПроизводительность, шаблоныЭкономичность

Контрольные списки реализации

Singleton

  • Приватный конструктор
  • Статический экземпляр
  • Потокобезопасность
  • Безопасность при сериализации
  • Защита от клонирования

Factory Method

  • Абстрактный фабричный метод
  • Конкретные реализации
  • Иерархия продуктов
  • Слабая связанность

Abstract Factory

  • Интерфейс фабрики
  • Конкретные фабрики
  • Семейства продуктов
  • Согласованность

Builder

  • Fluent Interface
  • Неизменяемый продукт
  • Валидация
  • Разделение обязательных и опциональных параметров

Prototype

  • Интерфейс Cloneable
  • Глубокое копирование
  • Реестр прототипов
  • Оптимизация производительности

Преимущества и недостатки

Преимущества порождающих паттернов

  • Гибкость: Легко адаптировать логику создания объектов
  • Переиспользуемость: Переиспользуемая логика создания
  • Инкапсуляция: Сложная логика скрыта
  • Тестируемость: Проще создавать моки
  • Поддерживаемость: Централизованное создание

Недостатки

  • Сложность: Дополнительные классы и интерфейсы
  • Затраты: Больше кода для простых случаев
  • Кривая обучения: Требуется понимание паттернов
  • Переусложнение: Риск чрезмерно сложных решений

Часто задаваемые вопросы на собеседованиях

  1. Когда использовать Singleton вместо Static Class? Singleton подходит для объектно-ориентированных особенностей вроде наследования, интерфейсов, ленивой инициализации.

  2. В чём разница между Factory Method и Abstract Factory? Factory Method создаёт один продукт, Abstract Factory создаёт семейства продуктов.

  3. Объясните преимущество паттерна Builder! Пошаговое конструирование сложных объектов с Fluent Interface и валидацией.

  4. Когда полезен паттерн Prototype? При дорогостоящем создании объектов или когда нужно множество похожих экземпляров.

Основные источники

  1. https://refactoring.guru/design-patterns/creational-patterns
  2. https://www.oracle.com/technetwork/java/designpatterns-138485.html
  3. https://en.wikipedia.org/wiki/Creational_pattern

Рекомендуемая литература: Design Patterns

Keine Bücher für Kategorie "design-patterns" gefunden.

Назад к блогу
Share:

Похожие статьи