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
- Singleton: Globale Instanz mit kontrolliertem Zugriff
- Factory Method: Abstrahierte Objekterstellung
- Abstract Factory: Interface für Objektfamilien
- Builder: Fluent Interface für komplexe Konstruktion
- Prototype: Klon-basierte Objekterstellung
- Dependency Injection: Inversion of Control
- Factory Configuration: Konfigurationsbasierte Erstellung
- 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
| Pattern | Zweck | Wann verwenden | Vorteile |
|---|---|---|---|
| Singleton | Eine Instanz sicherstellen | Logger, Konfiguration | Globaler Zugriff, Speichereffizienz |
| Factory Method | Objekterstellung delegieren | Frameworks, Plugins | Loose Coupling, Erweiterbarkeit |
| Abstract Factory | Objektfamilien erstellen | UI-Toolkits, Datenbank | Konsistente Objekte |
| Builder | Komplexe Objekte schrittweise bauen | Konfiguration, DSLs | Lesbarkeit, Flexibilität |
| Prototype | Objekte durch Klonen erstellen | Performance, Templates | Kosteneffizienz |
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
-
Wann verwendet man Singleton statt Static Class? Singleton für OO-Features wie Vererbung, Interfaces, Lazy Initialization.
-
Was ist der Unterschied zwischen Factory Method und Abstract Factory? Factory Method erstellt ein Produkt, Abstract Factory erstellt Familien von Produkten.
-
Erklären Sie den Vorteil des Builder Patterns! Schrittweise Konstruktion komplexer Objekte mit Fluent Interface und Validierung.
-
Wann ist Prototype Pattern nützlich? Bei teurer Objekterstellung oder wenn viele ähnliche Objekte benötigt werden.
Wichtigste Quellen
- https://refactoring.guru/design-patterns/creational-patterns
- https://www.oracle.com/technetwork/java/designpatterns-138485.html
- https://en.wikipedia.org/wiki/Creational_pattern