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: важно для гибкой архитектуры
Основные компоненты
- Singleton: глобальная инстанция с управляемым доступом
- Factory Method: абстрагированное создание объектов
- Abstract Factory: интерфейс для семейств объектов
- Builder: Fluent Interface для сложной конструкции
- Prototype: создание на основе клонирования
- Dependency Injection: инверсия управления
- Factory Configuration: создание на основе конфигурации
- 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
- Глубокое копирование
- Реестр прототипов
- Оптимизация производительности
Преимущества и недостатки
Преимущества порождающих паттернов
- Гибкость: Легко адаптировать логику создания объектов
- Переиспользуемость: Переиспользуемая логика создания
- Инкапсуляция: Сложная логика скрыта
- Тестируемость: Проще создавать моки
- Поддерживаемость: Централизованное создание
Недостатки
- Сложность: Дополнительные классы и интерфейсы
- Затраты: Больше кода для простых случаев
- Кривая обучения: Требуется понимание паттернов
- Переусложнение: Риск чрезмерно сложных решений
Часто задаваемые вопросы на собеседованиях
-
Когда использовать Singleton вместо Static Class? Singleton подходит для объектно-ориентированных особенностей вроде наследования, интерфейсов, ленивой инициализации.
-
В чём разница между Factory Method и Abstract Factory? Factory Method создаёт один продукт, Abstract Factory создаёт семейства продуктов.
-
Объясните преимущество паттерна Builder! Пошаговое конструирование сложных объектов с Fluent Interface и валидацией.
-
Когда полезен паттерн Prototype? При дорогостоящем создании объектов или когда нужно множество похожих экземпляров.
Основные источники
- https://refactoring.guru/design-patterns/creational-patterns
- https://www.oracle.com/technetwork/java/designpatterns-138485.html
- https://en.wikipedia.org/wiki/Creational_pattern
Рекомендуемая литература: Design Patterns
Keine Bücher für Kategorie "design-patterns" gefunden.



