Skip to content
IRC-Coding IRC-Coding
Design Patterns Creational Patterns Singleton Factory Builder Prototype Abstract Factory

Design Patterns: Creational Patterns - Singleton, Factory, Builder, Prototype & Abstract Factory

Creational Design Patterns mit Singleton, Factory, Builder, Prototype und Abstract Factory. Erzeugungsmuster für flexible Objekterstellung mit praktischen Java-Beispielen.

S

schutzgeist

2 min read

Design Patterns: Creational Patterns - Singleton, Factory, Builder, Prototype & Abstract Factory

Dieser Beitrag ist eine umfassende Anleitung zu den Creational Design Patterns – inklusive Singleton, Factory, Builder, Prototype und Abstract Factory mit praktischen Beispielen.

In a Nutshell

Creational Patterns abstrahieren Objekterstellung. Singleton sichert eine Instanz, Factory kapselt Erstellungslogik, Builder ermöglicht schrittweise Konstruktion, Prototype klont Objekte und Abstract Factory erstellt Familien verwandter Objekte.

Kompakte Fachbeschreibung

Creational Design Patterns sind Erzeugungsmuster, die den Prozess der Objektinstanziierung kontrollieren und flexibler gestalten.

Wichtige Creational Patterns:

Singleton Pattern

  • Zweck: Sicherstellung einer einzigen Instanz
  • Anwendung: Logger, Konfiguration, Thread-Pools
  • Implementierung: Private Konstruktor, statische Instanz
  • Thread-Safety: Synchronized oder eager initialization

Factory Method Pattern

  • Zweck: Objekterstellung durch Unterklassen delegieren
  • Anwendung: Frameworks, Plugin-Systeme
  • Implementierung: Abstrakte Factory-Methode
  • Vorteil: Loose Coupling, Erweiterbarkeit

Abstract Factory Pattern

  • Zweck: Familien verwandter Objekte erstellen
  • Anwendung: UI-Toolkits, Datenbank-Treiber
  • Implementierung: Factory-Interface mit konkreten Factories
  • Vorteil: Konsistente Objektfamilien

Builder Pattern

  • Zweck: Schrittweise Konstruktion komplexer Objekte
  • Anwendung: Konfigurationsobjekte, DSLs
  • Implementierung: Fluent Interface, separate Builder-Klasse
  • Vorteil: Lesbarkeit, Flexibilität

Prototype Pattern

  • Zweck: Objekte durch Klonen erstellen
  • Anwendung: Performance-optimierte Erstellung
  • Implementierung: Cloneable Interface, clone()-Methode
  • Vorteil: Kostenersparnis bei teurer Erstellung

Prüfungsrelevante Stichpunkte

  • Creational Patterns: Erzeugungsmuster für flexible Objektinstanziierung
  • Singleton: Nur eine Instanz, globale Zugriffspunkte
  • Factory Method: Objekterstellung durch Unterklassen
  • Abstract Factory: Familien von Objekten erstellen
  • Builder: Schrittweise Konstruktion komplexer Objekte
  • Prototype: Objekte durch Klonen erstellen
  • IHK-relevant: Wichtig für flexible Softwarearchitektur

Kernkomponenten

  1. Singleton: Globale Instanz mit kontrolliertem Zugriff
  2. Factory Method: Abstrahierte Objekterstellung
  3. Abstract Factory: Interface für Objektfamilien
  4. Builder: Fluent Interface für komplexe Konstruktion
  5. Prototype: Klon-basierte Objekterstellung
  6. Dependency Injection: Inversion of Control
  7. Factory Configuration: Konfigurationsbasierte Erstellung
  8. Object Pool: Wiederverwendung von Objekten

Praxisbeispiele

1. Singleton Pattern

// Eager Initialization (Thread-Safe)
public class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    private EagerSingleton() {
        // Private Konstruktor verhindert Instanziierung
    }
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
    
    public void doSomething() {
        System.out.println("Eager Singleton arbeitet");
    }
}

// Lazy Initialization mit Double-Checked Locking
public class LazySingleton {
    private static volatile LazySingleton instance;
    
    private LazySingleton() {
        // Private Konstruktor
    }
    
    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 in Aktion");
    }
}

// Singleton mit Enum (Thread-Safe, Serialization-Safe)
public enum EnumSingleton {
    INSTANCE;
    
    public void doWork() {
        System.out.println("Enum Singleton arbeitet");
    }
}

// Logger als 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 Demo
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 - Gleiche Instanz: " + (eager1 == eager2));
        eager1.doSomething();
        
        // Lazy Singleton
        LazySingleton lazy1 = LazySingleton.getInstance();
        LazySingleton lazy2 = LazySingleton.getInstance();
        
        System.out.println("Lazy Singleton - Gleiche Instanz: " + (lazy1 == lazy2));
        lazy2.performAction();
        
        // Enum Singleton
        EnumSingleton enum1 = EnumSingleton.INSTANCE;
        EnumSingleton enum2 = EnumSingleton.INSTANCE;
        
        System.out.println("Enum Singleton - Gleiche Instanz: " + (enum1 == enum2));
        enum1.doWork();
        
        // Logger Singleton
        Logger logger1 = Logger.getInstance();
        Logger logger2 = Logger.getInstance();
        
        System.out.println("Logger Singleton - Gleiche Instanz: " + (logger1 == logger2));
        
        logger1.log("Anwendung gestartet");
        logger2.log("Benutzer angemeldet");
        
        System.out.println("Logs: " + logger1.getLogs());
    }
}

2. Factory Method Pattern

// Abstrakte Produktklasse
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;
    }
}

// Konkrete Produkte
class Auto extends Fahrzeug {
    public Auto(String marke, String modell) {
        super(marke, modell);
    }
    
    @Override
    public void starten() {
        System.out.println("Auto " + this + " gestartet");
    }
    
    @Override
    public void bremsen() {
        System.out.println("Auto " + this + " gebremst");
    }
}

class Motorrad extends Fahrzeug {
    public Motorrad(String marke, String modell) {
        super(marke, modell);
    }
    
    @Override
    public void starten() {
        System.out.println("Motorrad " + this + " gestartet");
    }
    
    @Override
    public void bremsen() {
        System.out.println("Motorrad " + this + " gebremst");
    }
}

// Abstrakte Factory-Klasse
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("Produziert: " + fahrzeug);
        
        // Qualitätstest
        fahrzeug.starten();
        fahrzeug.bremsen();
        System.out.println("Test bestanden");
    }
}

// Konkrete Factories
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 mit Parameter
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("Unbekannter Fahrzeugtyp: " + typ);
        }
    }
}

// Factory Method Demo
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 Produktion ---");
        autoFabrik.produziereUndTeste("VW", "Golf");
        
        System.out.println("\n--- Motorrad Produktion ---");
        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 mit Konfiguration
        System.out.println("\n--- Factory mit Konfiguration ---");
        FahrzeugKonfigurator konfigurator = new FahrzeugKonfigurator();
        
        Fahrzeug auto1 = konfigurator.erstelleFahrzeug("auto", "Mercedes", "C-Klasse");
        Fahrzeug motorrad1 = konfigurator.erstelleFahrzeug("motorrad", "Harley", "Sportster");
        
        auto1.starten();
        motorrad1.starten();
    }
}

// Factory mit Konfiguration
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("Unbekannter Typ: " + 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();
    }
}

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());
        }
    }
}

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());
    }
}

Creational Patterns Übersicht

PatternZweckWann verwendenVorteile
SingletonEine Instanz sicherstellenLogger, KonfigurationGlobaler Zugriff, Speichereffizienz
Factory MethodObjekterstellung delegierenFrameworks, PluginsLoose Coupling, Erweiterbarkeit
Abstract FactoryObjektfamilien erstellenUI-Toolkits, DatenbankKonsistente Objekte
BuilderKomplexe Objekte schrittweise bauenKonfiguration, DSLsLesbarkeit, Flexibilität
PrototypeObjekte durch Klonen erstellenPerformance, TemplatesKosteneffizienz

Implementierungs-Checklisten

Singleton

  • Private Konstruktor
  • Statische Instanz
  • Thread-Safety
  • Serialization-Safety
  • Clone-Schutz

Factory Method

  • Abstrakte Factory-Methode
  • Konkrete Implementierungen
  • Produkt-Hierarchie
  • Loose Coupling

Abstract Factory

  • Factory Interface
  • Konkrete Factories
  • Produkt-Familien
  • Konsistenz

Builder

  • Fluent Interface
  • Immutable Produkt
  • Validierung
  • Required/Optional Trennung

Prototype

  • Cloneable Interface
  • Deep Clone
  • Prototype Registry
  • Performance-Optimierung

Vorteile und Nachteile

Vorteile von Creational Patterns

  • Flexibilität: Leichte Anpassung der Objekterstellung
  • Wiederverwendbarkeit: Wiederverwendbare Erstellungslogik
  • Kapselung: Komplexe Logik versteckt
  • Testbarkeit: Leichtere Mocking-Möglichkeiten
  • Maintainability: Zentralisierte Erstellung

Nachteile

  • Komplexität: Zusätzliche Klassen und Interfaces
  • Overhead: Mehr Code für einfache Fälle
  • Lernkurve: Verständnis der Patterns erforderlich
  • Over-engineering: Gefahr bei zu komplexen Lösungen

Häufige Prüfungsfragen

  1. Wann verwendet man Singleton statt Static Class? Singleton für OO-Features wie Vererbung, Interfaces, Lazy Initialization.

  2. Was ist der Unterschied zwischen Factory Method und Abstract Factory? Factory Method erstellt ein Produkt, Abstract Factory erstellt Familien von Produkten.

  3. Erklären Sie den Vorteil des Builder Patterns! Schrittweise Konstruktion komplexer Objekte mit Fluent Interface und Validierung.

  4. Wann ist Prototype Pattern nützlich? Bei teurer Objekterstellung oder wenn viele ähnliche Objekte benötigt werden.

Wichtigste Quellen

  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
Zurück zum Blog
Share:

Ähnliche Beiträge