Add all 23 GoF design pattern implementations
This commit is contained in:
8
01-creational/prototype/Copyable.java
Normal file
8
01-creational/prototype/Copyable.java
Normal 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();
|
||||
}
|
||||
46
01-creational/prototype/Document.java
Normal file
46
01-creational/prototype/Document.java
Normal 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);
|
||||
}
|
||||
}
|
||||
39
01-creational/prototype/DocumentMetadata.java
Normal file
39
01-creational/prototype/DocumentMetadata.java
Normal 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);
|
||||
}
|
||||
}
|
||||
21
01-creational/prototype/DocumentRegistry.java
Normal file
21
01-creational/prototype/DocumentRegistry.java
Normal 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
|
||||
}
|
||||
}
|
||||
47
01-creational/prototype/Main.java
Normal file
47
01-creational/prototype/Main.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package prototype;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
System.out.println("Original: " + original);
|
||||
|
||||
// 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("Draft: " + draft);
|
||||
System.out.println("Original: " + original); // unchanged — deep copy worked
|
||||
|
||||
// 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
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
32
01-creational/prototype/README.md
Normal file
32
01-creational/prototype/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Prototype Design Pattern — Java Example
|
||||
|
||||
**Pattern:** Creational → Prototype
|
||||
**Article:** https://ankurm.com/prototype-design-pattern-java/
|
||||
|
||||
## What this example shows
|
||||
|
||||
Documents cloned from existing instances instead of rebuilt from scratch. `Copyable` declares the cloning contract. `DocumentMetadata` is a nested object that must be copied correctly for a clone to be truly independent of its source. `Document` is the concrete prototype, implementing a real (not shallow) copy. `DocumentRegistry` stores a set of ready-made prototypes that callers clone on demand instead of constructing from raw parameters. `Main` demonstrates cloning from the registry and confirms the clone and original don't share mutable state.
|
||||
|
||||
## How to run
|
||||
|
||||
```bash
|
||||
javac prototype/*.java -d out/prototype
|
||||
java -cp out/prototype prototype.Main
|
||||
```
|
||||
|
||||
Requires Java 25.
|
||||
|
||||
## Post Section ↔ File Mapping
|
||||
|
||||
| Post Section | File(s) |
|
||||
|---|---|
|
||||
| Part 1 — The Prototype Interface: Copyable | `Copyable.java` |
|
||||
| Part 2 — The Nested Object: DocumentMetadata | `DocumentMetadata.java` |
|
||||
| Part 3 — The Concrete Prototype: Document | `Document.java` |
|
||||
| Part 4 — The Prototype Registry: DocumentRegistry | `DocumentRegistry.java` |
|
||||
| Part 5 — Using the Prototype: Client Code | `Main.java` |
|
||||
|
||||
Note: the `ShallowVsDeepDemo` snippet (shallow vs. deep copy) and the `Cloneable`-based example are illustrative only — they are not part of this repository's runnable example.
|
||||
|
||||
Article: https://ankurm.com/prototype-design-pattern-java/
|
||||
All patterns: https://ankurm.com/design-patterns-java/
|
||||
Reference in New Issue
Block a user