Sync Factory Method, Abstract Factory, Builder, Prototype to Java 25; fix post/code mismatches

This commit is contained in:
2026-06-23 11:00:02 +05:30
parent 2f684bf3d7
commit 2fbf89875b
51 changed files with 634 additions and 337 deletions

View File

@@ -1,21 +1,30 @@
package abstractfactory;
/** Client - uses the factory without knowing concrete classes */
public class Application {
private final Button button;
private final Checkbox checkbox;
public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
// The client holds references to the Abstract Factory and Abstract Products.
// It never imports LightButton, DarkButton, or any concrete class.
private final UIFactory factory;
private Button submitButton;
private TextField usernameField;
public Application(UIFactory factory) {
this.factory = factory; // theme is injected; client doesn't decide
}
public void buildUI() {
// Ask the factory — get consistent products from the same family
submitButton = factory.createButton();
usernameField = factory.createTextField();
}
public void render() {
button.render();
checkbox.render();
System.out.println("Rendering login screen:");
usernameField.render();
submitButton.render();
}
public void simulateClick() {
button.onClick();
public void simulateSubmit() {
submitButton.onClick();
}
}

View File

@@ -1,6 +1,7 @@
package abstractfactory;
// Every button, regardless of theme, must be able to render and handle clicks
public interface Button {
void render();
void onClick();
void render(); // draw on screen
void onClick(); // respond to a click event
}

View File

@@ -1,5 +0,0 @@
package abstractfactory;
public interface Checkbox {
void render();
}

View File

@@ -0,0 +1,11 @@
package abstractfactory;
public class DarkButton implements Button {
@Override
public void render() {
System.out.println("[Dark Button] Charcoal background, white text, no border");
}
@Override
public void onClick() {
System.out.println("[Dark Button] Bright highlight ripple effect");
}
}

View File

@@ -0,0 +1,10 @@
package abstractfactory;
public class DarkTextField implements TextField {
private String value = "";
@Override
public void render() {
System.out.println("[Dark TextField] Dark gray background, light placeholder text");
}
@Override
public String getValue() { return value; }
}

View File

@@ -0,0 +1,14 @@
package abstractfactory;
public class DarkThemeFactory implements UIFactory {
@Override
public Button createButton() {
return new DarkButton(); // Only Dark products come from this factory
}
@Override
public TextField createTextField() {
return new DarkTextField();
}
}

View File

@@ -1,7 +0,0 @@
package abstractfactory;
/** Abstract Factory - creates families of related UI components */
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}

View File

@@ -0,0 +1,11 @@
package abstractfactory;
public class LightButton implements Button {
@Override
public void render() {
System.out.println("[Light Button] White background, dark text, 1px gray border");
}
@Override
public void onClick() {
System.out.println("[Light Button] Subtle gray ripple effect");
}
}

View File

@@ -0,0 +1,10 @@
package abstractfactory;
public class LightTextField implements TextField {
private String value = "";
@Override
public void render() {
System.out.println("[Light TextField] White background, dark placeholder text");
}
@Override
public String getValue() { return value; }
}

View File

@@ -0,0 +1,14 @@
package abstractfactory;
public class LightThemeFactory implements UIFactory {
@Override
public Button createButton() {
return new LightButton(); // Only Light products come from this factory
}
@Override
public TextField createTextField() {
return new LightTextField();
}
}

View File

@@ -1,6 +0,0 @@
package abstractfactory;
public class MacButton implements Button {
@Override public void render() { System.out.println("[Mac] Rendering button with rounded corners"); }
@Override public void onClick() { System.out.println("[Mac] Button clicked - glow animation"); }
}

View File

@@ -1,5 +0,0 @@
package abstractfactory;
public class MacCheckbox implements Checkbox {
@Override public void render() { System.out.println("[Mac] Rendering checkbox with rounded tick box"); }
}

View File

@@ -1,6 +0,0 @@
package abstractfactory;
public class MacFactory implements GUIFactory {
@Override public Button createButton() { return new MacButton(); }
@Override public Checkbox createCheckbox() { return new MacCheckbox(); }
}

View File

@@ -1,22 +1,16 @@
package abstractfactory;
// Entry point: the ONLY place that knows the theme
public class Main {
public static void main(String[] args) {
System.out.println("=== Abstract Factory Pattern Demo ===\n");
String os = System.getProperty("os.name", "Windows").toLowerCase();
GUIFactory factory = os.contains("mac") ? new MacFactory() : new WindowsFactory();
System.out.println("Detected OS family: " + (os.contains("mac") ? "Mac" : "Windows"));
System.out.println();
// Production: read theme from config or OS preference
String theme = System.getProperty("app.theme", "light");
UIFactory factory = "dark".equals(theme) ? new DarkThemeFactory() : new LightThemeFactory();
Application app = new Application(factory);
app.buildUI();
app.render();
app.simulateClick();
System.out.println("\n--- Forcing Mac UI ---");
Application macApp = new Application(new MacFactory());
macApp.render();
macApp.simulateClick();
app.simulateSubmit();
}
}

View File

@@ -0,0 +1,7 @@
package abstractfactory;
// Every text field must be able to render and return its value
public interface TextField {
void render();
String getValue();
}

View File

@@ -0,0 +1,13 @@
package abstractfactory;
public interface UIFactory {
// Factory method for buttons — returns the abstract type, not LightButton or DarkButton
Button createButton();
// Factory method for text fields — same principle
TextField createTextField();
// Adding a new product type (e.g., Checkbox) means adding a method here
// AND implementing it in every Concrete Factory. This is the one cost of the pattern.
}

View File

@@ -1,6 +0,0 @@
package abstractfactory;
public class WindowsButton implements Button {
@Override public void render() { System.out.println("[Windows] Rendering button with square corners"); }
@Override public void onClick() { System.out.println("[Windows] Button clicked - raised border effect"); }
}

View File

@@ -1,5 +0,0 @@
package abstractfactory;
public class WindowsCheckbox implements Checkbox {
@Override public void render() { System.out.println("[Windows] Rendering checkbox with square tick box"); }
}

View File

@@ -1,6 +0,0 @@
package abstractfactory;
public class WindowsFactory implements GUIFactory {
@Override public Button createButton() { return new WindowsButton(); }
@Override public Checkbox createCheckbox() { return new WindowsCheckbox(); }
}

View File

@@ -1,12 +0,0 @@
package builder;
/** Director knows how to build common configurations */
public class Director {
public House buildStarter(House.Builder builder) {
return builder.rooms(2).floors(1).garage(false).roofType("gabled").build();
}
public House buildLuxury(House.Builder builder) {
return builder.rooms(6).floors(3).garage(true).garden(true).pool(true).roofType("hip").build();
}
}

View File

@@ -1,43 +0,0 @@
package builder;
public class House {
private final int rooms;
private final int floors;
private final boolean hasGarage;
private final boolean hasGarden;
private final boolean hasPool;
private final String roofType;
private House(Builder builder) {
this.rooms = builder.rooms;
this.floors = builder.floors;
this.hasGarage = builder.hasGarage;
this.hasGarden = builder.hasGarden;
this.hasPool = builder.hasPool;
this.roofType = builder.roofType;
}
@Override
public String toString() {
return "House{rooms=" + rooms + ", floors=" + floors
+ ", garage=" + hasGarage + ", garden=" + hasGarden
+ ", pool=" + hasPool + ", roof='" + roofType + "'}";
}
public static class Builder {
private int rooms = 1;
private int floors = 1;
private boolean hasGarage = false;
private boolean hasGarden = false;
private boolean hasPool = false;
private String roofType = "flat";
public Builder rooms(int rooms) { this.rooms = rooms; return this; }
public Builder floors(int floors) { this.floors = floors; return this; }
public Builder garage(boolean v) { this.hasGarage = v; return this; }
public Builder garden(boolean v) { this.hasGarden = v; return this; }
public Builder pool(boolean v) { this.hasPool = v; return this; }
public Builder roofType(String roofType) { this.roofType = roofType; return this; }
public House build() { return new House(this); }
}
}

View File

@@ -0,0 +1,27 @@
package builder;
public class HttpClientConfig {
// Director method 1: a health check probe — short timeout, no retries
public HttpRequest buildHealthCheck(String baseUrl) {
return new HttpRequest.Builder()
.url(baseUrl + "/health")
.method("GET")
.timeoutMs(2_000) // fast fail for health checks
.maxRetries(0)
.build();
}
// Director method 2: a resilient POST with standard JSON headers and retries
public HttpRequest buildResilientPost(String url, String jsonBody) {
return new HttpRequest.Builder()
.url(url)
.method("POST")
.body(jsonBody)
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.timeoutMs(15_000)
.maxRetries(3)
.build();
}
}

View File

@@ -0,0 +1,76 @@
package builder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public final class HttpRequest {
// All fields are final: the object is immutable after build() returns.
private final String url;
private final String method;
private final String body;
private final Map<String, String> headers;
private final int timeoutMs;
private final int maxRetries;
// Private: the only path to an HttpRequest is through Builder.build()
private HttpRequest(Builder builder) {
this.url = Objects.requireNonNull(builder.url, "URL is required");
this.method = builder.method;
this.body = builder.body;
this.headers = Collections.unmodifiableMap(new HashMap<>(builder.headers));
this.timeoutMs = builder.timeoutMs;
this.maxRetries = builder.maxRetries;
}
// Getters only — no setters, enforcing immutability
public String getUrl() { return url; }
public String getMethod() { return method; }
public String getBody() { return body; }
public Map<String, String> getHeaders() { return headers; }
public int getTimeoutMs() { return timeoutMs; }
public int getMaxRetries() { return maxRetries; }
@Override
public String toString() {
return String.format("%s %s (timeout=%dms, retries=%d, headers=%s, body=%s)",
method, url, timeoutMs, maxRetries, headers, body);
}
// -----------------------------------------------------------------------
// THE BUILDER — static inner class so it can call the private constructor
// -----------------------------------------------------------------------
public static class Builder {
// Required field — no default, must be set
private String url;
// Optional fields with sensible defaults
private String method = "GET";
private String body = null;
private Map<String, String> headers = new HashMap<>();
private int timeoutMs = 30_000; // 30 seconds
private int maxRetries = 0;
// Fluent methods: each returns 'this' so calls can be chained
public Builder url(String url) { this.url = url; return this; }
public Builder method(String method) { this.method = method; return this; }
public Builder body(String body) { this.body = body; return this; }
public Builder header(String key, String v) { headers.put(key, v); return this; }
public Builder timeoutMs(int ms) { this.timeoutMs = ms; return this; }
public Builder maxRetries(int n) { this.maxRetries = n; return this; }
// Terminal method: validates and creates the immutable HttpRequest
public HttpRequest build() {
if (url == null || url.isBlank()) {
throw new IllegalStateException("url() is required before calling build()");
}
if (maxRetries < 0) {
throw new IllegalStateException("maxRetries must be >= 0");
}
return new HttpRequest(this); // calls the private constructor
}
}
}

View File

@@ -1,20 +1,42 @@
package builder;
public class Main {
public static void main(String[] args) {
System.out.println("=== Builder Pattern Demo ===\n");
Director director = new Director();
// Simple GET — only the required field
HttpRequest ping = new HttpRequest.Builder()
.url("https://api.example.com/health")
.build();
House starter = director.buildStarter(new House.Builder());
System.out.println("Starter home : " + starter);
System.out.println("Simple GET: " + ping);
House luxury = director.buildLuxury(new House.Builder());
System.out.println("Luxury home : " + luxury);
// Full POST with authentication and retry
HttpRequest post = new HttpRequest.Builder()
.url("https://api.example.com/orders")
.method("POST")
.body("{\"item\":\"book\",\"qty\":1}")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer eyJ...")
.timeoutMs(10_000) // 10 seconds — name is self-documenting
.maxRetries(3)
.build();
// Client builds a custom house directly without the Director
House custom = new House.Builder()
.rooms(4).floors(2).garden(true).roofType("mansard").build();
System.out.println("Custom home : " + custom);
System.out.println("POST: " + post);
// build() throws if url is missing — validation at construction time
try {
HttpRequest bad = new HttpRequest.Builder().build(); // no URL!
} catch (IllegalStateException e) {
System.out.println("Caught: " + e.getMessage()); // url() is required
}
// Using the Director for the two standard presets defined above
HttpClientConfig config = new HttpClientConfig();
HttpRequest healthCheck = config.buildHealthCheck("https://api.example.com");
System.out.println("Health check: " + healthCheck);
HttpRequest order = config.buildResilientPost("https://api.example.com/orders", "{\"qty\":1}");
System.out.println("Order: " + order);
}
}

View File

@@ -1,11 +1,13 @@
package factorymethod;
public class EmailNotification implements Notification {
private final String email;
public EmailNotification(String email) { this.email = email; }
@Override
public void send(String message) {
System.out.println("Email -> " + email + ": " + message);
public void send(String recipient, String message) {
// In production: use JavaMail or Spring Mail
System.out.printf(" [EMAIL] To: %s | Body: %s%n", recipient, message);
}
@Override
public String getType() { return "EMAIL"; }
}

View File

@@ -0,0 +1,8 @@
package factorymethod;
public class EmailSender extends NotificationSender {
@Override
protected Notification createNotification() {
return new EmailNotification();
}
}

View File

@@ -1,11 +0,0 @@
package factorymethod;
public class EmailService extends NotificationService {
private final String email;
public EmailService(String email) { this.email = email; }
@Override
protected Notification createNotification() {
return new EmailNotification(email);
}
}

View File

@@ -1,17 +1,23 @@
package factorymethod;
public class Main {
public static void main(String[] args) {
System.out.println("=== Factory Method Pattern Demo ===\n");
NotificationService[] services = {
new EmailService("alice@example.com"),
new SmsService("+1-555-0123"),
new PushService("device-token-abc123")
};
// The client only knows about NotificationSender (the Creator)
NotificationSender sender;
for (NotificationService service : services) {
service.notifyUser("Your order #1042 has been shipped!");
}
sender = new EmailSender();
sender.notify("alice@example.com", "Your order has shipped!");
sender = new SmsSender();
sender.notify("+1-555-0100", "OTP: 482910");
sender = new PushSender();
sender.notify("device-token-xyz", "New message from Bob");
// Adding Slack: just swap the creator. Client code doesn't change.
sender = new SlackSender(); // new class, nothing else touched
sender.notify("#alerts", "CPU usage above 90%");
}
}

View File

@@ -1,5 +1,11 @@
package factorymethod;
public interface Notification {
void send(String message);
// Every notification must know how to deliver itself
void send(String recipient, String message);
// Used by the Creator for logging — it needs to know the type
// without knowing the concrete class
String getType();
}

View File

@@ -0,0 +1,31 @@
package factorymethod;
public abstract class NotificationSender {
// THE factory method — this is the one thing subclasses override.
// It returns a Notification (the Product interface), never a concrete class.
protected abstract Notification createNotification();
// The shared workflow — marked final so subclasses cannot change it.
// It calls createNotification() to get the right notification,
// then runs the same steps every time regardless of which channel was created.
public final void notify(String recipient, String message) {
Notification notification = createNotification(); // delegate creation
System.out.println("Sending " + notification.getType() + " notification...");
notification.send(recipient, message); // use the Product interface
System.out.println("Delivered.");
}
// Shared retry logic — works for all channels without change
public void notifyWithRetry(String recipient, String message, int maxRetries) {
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
notify(recipient, message);
return; // success — exit
} catch (Exception e) {
System.err.printf("Attempt %d failed: %s%n", attempt, e.getMessage());
}
}
throw new RuntimeException("All " + maxRetries + " attempts failed");
}
}

View File

@@ -1,11 +1,24 @@
package factorymethod;
/** Creator - declares the factory method */
public abstract class NotificationService {
protected abstract Notification createNotification();
public class NotificationService {
public void notifyUser(String message) {
Notification n = createNotification();
n.send(message);
public void send(String type, String recipient, String message) {
// Creation logic mixed with business logic
Notification notification;
if ("EMAIL".equals(type)) {
notification = new EmailNotification();
} else if ("SMS".equals(type)) {
notification = new SmsNotification();
} else if ("PUSH".equals(type)) {
notification = new PushNotification();
} else {
throw new IllegalArgumentException("Unknown type: " + type);
}
// Business logic that should never change
System.out.println("Sending " + type + " to " + recipient);
notification.send(recipient, message);
System.out.println("Delivered.");
}
}

View File

@@ -1,11 +1,13 @@
package factorymethod;
public class PushNotification implements Notification {
private final String deviceToken;
public PushNotification(String deviceToken) { this.deviceToken = deviceToken; }
@Override
public void send(String message) {
System.out.println("Push -> " + deviceToken + ": " + message);
public void send(String recipient, String message) {
// In production: use Firebase FCM
System.out.printf(" [PUSH] To: %s | Alert: %s%n", recipient, message);
}
@Override
public String getType() { return "PUSH"; }
}

View File

@@ -0,0 +1,8 @@
package factorymethod;
public class PushSender extends NotificationSender {
@Override
protected Notification createNotification() {
return new PushNotification();
}
}

View File

@@ -1,11 +0,0 @@
package factorymethod;
public class PushService extends NotificationService {
private final String token;
public PushService(String token) { this.token = token; }
@Override
protected Notification createNotification() {
return new PushNotification(token);
}
}

View File

@@ -0,0 +1,11 @@
package factorymethod;
// New Concrete Product — knows how to send to Slack
public class SlackNotification implements Notification {
@Override
public void send(String recipient, String message) {
System.out.printf(" [SLACK] Channel: %s | Message: %s%n", recipient, message);
}
@Override
public String getType() { return "SLACK"; }
}

View File

@@ -0,0 +1,9 @@
package factorymethod;
// New Concrete Creator — knows to create a SlackNotification
public class SlackSender extends NotificationSender {
@Override
protected Notification createNotification() {
return new SlackNotification();
}
}

View File

@@ -1,11 +1,13 @@
package factorymethod;
public class SmsNotification implements Notification {
private final String phone;
public SmsNotification(String phone) { this.phone = phone; }
@Override
public void send(String message) {
System.out.println("SMS -> " + phone + ": " + message);
public void send(String recipient, String message) {
// In production: use Twilio SDK
System.out.printf(" [SMS] To: %s | Text: %s%n", recipient, message);
}
@Override
public String getType() { return "SMS"; }
}

View File

@@ -0,0 +1,8 @@
package factorymethod;
public class SmsSender extends NotificationSender {
@Override
protected Notification createNotification() {
return new SmsNotification();
}
}

View File

@@ -1,11 +0,0 @@
package factorymethod;
public class SmsService extends NotificationService {
private final String phone;
public SmsService(String phone) { this.phone = phone; }
@Override
protected Notification createNotification() {
return new SmsNotification(phone);
}
}

View File

@@ -1,24 +0,0 @@
package prototype;
public class Circle extends Shape {
private int radius;
public Circle(int radius, String color) {
this.radius = radius;
this.color = color;
}
/** Copy constructor used by clone() */
private Circle(Circle source) {
super(source);
this.radius = source.radius;
}
@Override
public Circle clone() { return new Circle(this); }
@Override
public String toString() {
return "Circle{color=" + color + ", radius=" + radius + ", pos=(" + x + "," + y + ")}";
}
}

View File

@@ -0,0 +1,8 @@
package prototype;
// Our Prototype interface: any class that can clone itself implements this.
// The return type T allows each implementing class to return its own type,
// not the raw interface type, which makes the client code cleaner.
public interface Copyable<T> {
T copy();
}

View File

@@ -0,0 +1,46 @@
package prototype;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Document implements Copyable<Document> {
private String title; // String: immutable, safe to share on copy
private String content; // String: immutable, safe to share on copy
private List<String> tags; // List: mutable, MUST be deep-copied
private DocumentMetadata metadata; // Mutable object, MUST be deep-copied
public Document(String title, String content, List<String> tags, DocumentMetadata metadata) {
this.title = title;
this.content = content;
this.tags = new ArrayList<>(tags); // defensive copy on construction
this.metadata = metadata;
}
// Copy constructor: the preferred Prototype implementation in modern Java
public Document(Document source) {
this.title = source.title; // immutable: share
this.content = source.content; // immutable: share
this.tags = new ArrayList<>(source.tags); // mutable: new list
this.metadata = source.metadata.copy(); // mutable: deep copy via Copyable
}
@Override
public Document copy() {
return new Document(this); // delegates to copy constructor
}
// Getters and setters
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public List<String> getTags() { return Collections.unmodifiableList(tags); }
public void addTag(String tag) { tags.add(tag); }
public DocumentMetadata getMetadata() { return metadata; }
@Override public String toString() {
return String.format("Document{title='%s', tags=%s, %s}", title, tags, metadata);
}
}

View File

@@ -0,0 +1,39 @@
package prototype;
import java.time.LocalDate;
public class DocumentMetadata implements Copyable<DocumentMetadata> {
private String author;
private LocalDate createdDate; // LocalDate is immutable — safe to share
private String version;
public DocumentMetadata(String author, LocalDate createdDate, String version) {
this.author = author;
this.createdDate = createdDate; // safe: LocalDate is immutable
this.version = version;
}
// Copy constructor: creates a new instance with the same values
public DocumentMetadata(DocumentMetadata source) {
this.author = source.author; // String: immutable, safe to share
this.createdDate = source.createdDate; // LocalDate: immutable, safe to share
this.version = source.version;
}
@Override
public DocumentMetadata copy() {
return new DocumentMetadata(this); // delegates to copy constructor
}
public String getAuthor() { return author; }
public LocalDate getCreatedDate(){ return createdDate; }
public String getVersion() { return version; }
public void setVersion(String v) { this.version = v; }
public void setAuthor(String a) { this.author = a; }
@Override public String toString() {
return String.format("Metadata{author='%s', date=%s, version='%s'}",
author, createdDate, version);
}
}

View File

@@ -0,0 +1,21 @@
package prototype;
import java.util.HashMap;
import java.util.Map;
public class DocumentRegistry {
private final Map<String, Document> prototypes = new HashMap<>();
// Register a prototype under a name
public void register(String name, Document prototype) {
prototypes.put(name, prototype);
}
// Return a fresh copy of the named prototype
public Document get(String name) {
Document prototype = prototypes.get(name);
if (prototype == null) throw new IllegalArgumentException("No prototype: " + name);
return prototype.copy(); // always return a copy, never the prototype itself
}
}

View File

@@ -1,33 +1,47 @@
package prototype;
import java.util.ArrayList;
import java.time.LocalDate;
import java.util.List;
public class Main {
public static void main(String[] args) {
System.out.println("=== Prototype Pattern Demo ===\n");
List<Shape> originals = new ArrayList<>();
originals.add(new Circle(10, "red"));
originals.add(new Rectangle(20, 30, "blue"));
// Create the original document (imagine this was loaded from a database)
DocumentMetadata metadata = new DocumentMetadata("Alice", LocalDate.of(2025, 1, 15), "1.0");
Document original = new Document(
"Q1 Report",
"Revenue increased by 12%...",
List.of("finance", "quarterly"),
metadata
);
// Clone each shape - no need to know the concrete type
List<Shape> copies = new ArrayList<>();
for (Shape shape : originals) {
copies.add(shape.clone());
}
System.out.println("Original: " + original);
// Mutate copies - originals are unaffected
copies.get(0).setColor("green");
copies.get(0).move(5, 5);
copies.get(1).setColor("yellow");
// Clone the document and apply only the differences
Document draft = original.copy();
draft.setTitle("Q1 Report — DRAFT");
draft.addTag("draft");
draft.getMetadata().setAuthor("Bob"); // only the copy's metadata changes
draft.getMetadata().setVersion("1.0-draft");
System.out.println("--- Originals ---");
originals.forEach(System.out::println);
System.out.println("Draft: " + draft);
System.out.println("Original: " + original); // unchanged — deep copy worked
System.out.println("\n--- Clones (mutated) ---");
copies.forEach(System.out::println);
// Verify independence
System.out.println("\nSame object? " + (original == draft)); // false
System.out.println("Same tags? " + (original.getTags() == draft.getTags())); // false
System.out.println("Same meta? " + (original.getMetadata() == draft.getMetadata())); // false
System.out.println("\nSame instance? " + (originals.get(0) == copies.get(0)));
// Registry demo: register a named template once, then hand out independent copies
DocumentRegistry registry = new DocumentRegistry();
registry.register("q-report-template", new Document(
"Quarterly Report Template", "## Executive Summary\n...",
List.of("quarterly", "finance"), metadata));
Document myReport = registry.get("q-report-template"); // a fresh, independent copy
myReport.setTitle("Q2 2025 Report");
myReport.addTag("q2");
System.out.println("\nFrom registry: " + myReport);
}
}

View File

@@ -1,26 +0,0 @@
package prototype;
public class Rectangle extends Shape {
private int width;
private int height;
public Rectangle(int width, int height, String color) {
this.width = width;
this.height = height;
this.color = color;
}
private Rectangle(Rectangle source) {
super(source);
this.width = source.width;
this.height = source.height;
}
@Override
public Rectangle clone() { return new Rectangle(this); }
@Override
public String toString() {
return "Rectangle{color=" + color + ", size=" + width + "x" + height + ", pos=(" + x + "," + y + ")}";
}
}

View File

@@ -1,22 +0,0 @@
package prototype;
/** Prototype - every shape can clone itself */
public abstract class Shape {
protected String color;
protected int x;
protected int y;
protected Shape(Shape source) {
this.color = source.color;
this.x = source.x;
this.y = source.y;
}
protected Shape() {}
public abstract Shape clone();
public void setColor(String color) { this.color = color; }
public String getColor() { return color; }
public void move(int x, int y) { this.x = x; this.y = y; }
}

View File

@@ -0,0 +1,19 @@
package singleton;
public class AppConfig {
private AppConfig() {
System.out.println("Reading configuration from file...");
}
private static class Holder {
static final AppConfig INSTANCE = new AppConfig();
}
public static AppConfig getInstance() {
return Holder.INSTANCE;
}
public String getProperty(String key) {
return System.getProperty(key, "(not set)");
}
}

View File

@@ -1,34 +0,0 @@
package singleton;
/**
* Thread-safe Singleton using double-checked locking.
*/
public class DatabaseConnection {
private static volatile DatabaseConnection instance;
private final String url;
private int queryCount = 0;
private DatabaseConnection() {
this.url = "jdbc:mysql://localhost:3306/myapp";
System.out.println("Establishing connection to " + url);
}
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (DatabaseConnection.class) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
public String executeQuery(String sql) {
queryCount++;
return "Result #" + queryCount + " for: " + sql;
}
public int getQueryCount() { return queryCount; }
public String getUrl() { return url; }
}

View File

@@ -1,19 +1,9 @@
package singleton;
public class Main {
public static void main(String[] args) {
System.out.println("=== Singleton Pattern Demo ===\n");
DatabaseConnection c1 = DatabaseConnection.getInstance();
DatabaseConnection c2 = DatabaseConnection.getInstance();
DatabaseConnection c3 = DatabaseConnection.getInstance();
System.out.println("\nAll three references same instance? " + (c1 == c2 && c2 == c3));
System.out.println(c1.executeQuery("SELECT * FROM users"));
System.out.println(c2.executeQuery("SELECT * FROM orders"));
System.out.println(c3.executeQuery("SELECT COUNT(*) FROM products"));
System.out.println("\nTotal queries: " + c1.getQueryCount());
AppConfig config1 = AppConfig.getInstance();
AppConfig config2 = AppConfig.getInstance();
System.out.println("Same instance? " + (config1 == config2)); // true
System.out.println("db.url = " + config1.getProperty("db.url"));
}
}