diff --git a/02-structural/composite/Directory.java b/02-structural/composite/Directory.java index 5e638b5..7dca2ad 100644 --- a/02-structural/composite/Directory.java +++ b/02-structural/composite/Directory.java @@ -1,53 +1,39 @@ package composite; - import java.util.ArrayList; import java.util.List; - /** - * Composite — a directory that can hold both Files (leaves) - * and other Directories (composites). + * Composite — a directory that holds both Files (leaves) and other Directories. + * + * getSize() is recursive: asks each child for its size and sums them. + * print() recurses with deeper indentation. * - * getSize() is recursive: it asks each child for its size and sums them. * The caller doesn't care whether a child is a File or Directory — - * both implement FileSystemItem and answer getSize(). - * - * This is the power of Composite: uniform treatment of simple and complex. + * both answer getSize() and print() the same way. */ public class Directory implements FileSystemItem { - private final String name; private final List children = new ArrayList<>(); - - public Directory(String name) { - this.name = name; - } - + public Directory(String name) { this.name = name; } + // Fluent add() — allows chaining: dir.add(file1).add(file2).add(subDir) public Directory add(FileSystemItem item) { children.add(item); - return this; // fluent API for easy nesting + return this; } - - public void remove(FileSystemItem item) { - children.remove(item); - } - - @Override - public String getName() { return name; } - + public void remove(FileSystemItem item) { children.remove(item); } + @Override public String getName() { return name; } @Override public long getSize() { - // Recursion: each child knows its own size. - // Files return their bytes; directories sum their children. + // Each child knows its own size: files return bytes, directories recurse. + // This is the Composite's core: uniform delegation down the tree. return children.stream() .mapToLong(FileSystemItem::getSize) .sum(); } - @Override public void print(String indent) { System.out.printf("%s[DIR] %s/ (%,d bytes total)%n", indent, name, getSize()); for (FileSystemItem child : children) { - child.print(indent + " "); // recurse with deeper indent + child.print(indent + " "); // each level indents 4 more spaces } } } diff --git a/02-structural/composite/File.java b/02-structural/composite/File.java index 6ae4734..d9bfca9 100644 --- a/02-structural/composite/File.java +++ b/02-structural/composite/File.java @@ -1,25 +1,18 @@ package composite; - /** - * Leaf — a single file. It has no children. - * getSize() returns its own size. print() shows its name. - * - * Notice: the File has no knowledge of directories or nesting. - * It just knows its own name and size. + * Leaf — a single file with no children. + * getSize() returns its own bytes. print() outputs a single line. + * No knowledge of directories or nesting exists in this class. */ public class File implements FileSystemItem { - private final String name; private final long size; - public File(String name, long sizeBytes) { this.name = name; this.size = sizeBytes; } - @Override public String getName() { return name; } @Override public long getSize() { return size; } - @Override public void print(String indent) { System.out.printf("%s[FILE] %s (%,d bytes)%n", indent, name, size); diff --git a/02-structural/composite/FileSystemItem.java b/02-structural/composite/FileSystemItem.java index be0d5d6..cf6b68f 100644 --- a/02-structural/composite/FileSystemItem.java +++ b/02-structural/composite/FileSystemItem.java @@ -1,12 +1,10 @@ package composite; - /** - * Component interface — the common contract for BOTH files (leaves) - * and directories (composites). Clients work through this interface - * and never need to know which they're dealing with. + * Component — the common interface for both files (leaves) and directories (composites). + * Clients work through this interface and never need to know which type they're dealing with. */ public interface FileSystemItem { String getName(); - long getSize(); // total size in bytes (recursive for directories) - void print(String indent); // display the tree + long getSize(); // total size in bytes — recursive for directories + void print(String indent); // display the tree at the given indentation level } diff --git a/02-structural/composite/Main.java b/02-structural/composite/Main.java index fe19845..80181a0 100644 --- a/02-structural/composite/Main.java +++ b/02-structural/composite/Main.java @@ -1,58 +1,36 @@ package composite; - -/** - * Composite Design Pattern — Runnable Demo - * - * Builds a file system tree with nested directories and files. - * Demonstrates that getSize() and print() work uniformly on - * leaves (File) and composites (Directory) without any instanceof checks. - * - * Run: javac composite/*.java && java composite.Main - * Article: https://ankurm.com/composite-design-pattern-java/ - */ public class Main { - public static void main(String[] args) { System.out.println("=== Composite Design Pattern Demo ===\n"); - - // Build a file system tree + // Build the tree — mix files and directories freely Directory root = new Directory("project"); - Directory src = new Directory("src"); Directory main = new Directory("main"); - main.add(new File("App.java", 4_200)) - .add(new File("Config.java", 1_800)) - .add(new File("Application.yml", 3_100)); - + main.add(new File("App.java", 4_200)) + .add(new File("Config.java", 1_800)) + .add(new File("Application.yml", 3_100)); Directory test = new Directory("test"); - test.add(new File("AppTest.java", 2_600)) + test.add(new File("AppTest.java", 2_600)) .add(new File("ConfigTest.java", 1_200)); - src.add(main).add(test); - Directory resources = new Directory("resources"); - resources.add(new File("application.yml", 1_500)) + resources.add(new File("application.yml", 1_500)) .add(new File("logback.xml", 900)) - .add(new File("banner.txt", 200)); - + .add(new File("banner.txt", 200)); root.add(src) .add(resources) - .add(new File("pom.xml", 8_400)) - .add(new File("README.md", 2_100)); - - // Print entire tree — recursion happens automatically + .add(new File("pom.xml", 8_400)) + .add(new File("README.md", 2_100)); + // Print the entire tree — recursion is automatic System.out.println("File system tree:"); root.print(""); - System.out.printf("%nTotal project size: %,d bytes%n", root.getSize()); - - // Client treats File and Directory identically + // The key demonstration: File and Directory through the same interface System.out.println("\n-- Treating File and Directory uniformly --"); FileSystemItem[] items = { new File("standalone.txt", 500), src }; for (FileSystemItem item : items) { System.out.printf("%s -> size: %,d bytes%n", item.getName(), item.getSize()); } - System.out.println("\n=== Demo complete ==="); } } diff --git a/02-structural/composite/README.md b/02-structural/composite/README.md new file mode 100644 index 0000000..4f2c62e --- /dev/null +++ b/02-structural/composite/README.md @@ -0,0 +1,30 @@ +# Composite Design Pattern — Java Example + +**Pattern:** Structural → Composite +**Article:** https://ankurm.com/composite-design-pattern-java/ + +## What this example shows + +Builds a file system tree where files (leaves) and directories (composites) share the same `FileSystemItem` interface. Callers compute size or print the tree without ever checking whether a node is a file or a directory. + +## How to run + +```bash +javac composite/*.java -d out/composite +java -cp out/composite composite.Main +``` + +Requires Java 25. + +## Post Section ↔ File Mapping + +| Post Section | File(s) | +|---|---| +| The Problem: Treating Leaves and Containers Uniformly | illustrative only — not part of this repository's runnable example | +| Step 1 — The Component Interface | `FileSystemItem.java` | +| Step 2 — The Leaf (File) | `File.java` | +| Step 3 — The Composite (Directory) | `Directory.java` | +| Building and Using the Tree | `Main.java` | + +Article: https://ankurm.com/composite-design-pattern-java/ +All patterns: https://ankurm.com/design-patterns-java/ diff --git a/README.md b/README.md index c41c2c6..75b11e0 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,22 @@ javac 02-structural/bridge/*.java -d out/bridge java -cp out/bridge bridge.Main ``` +### Composite (`02-structural/composite/`) + +| Post Section | File(s) | +|---|---| +| The Problem: Treating Leaves and Containers Uniformly | illustrative only — not part of this repository's runnable example | +| Step 1 — The Component Interface | `FileSystemItem.java` | +| Step 2 — The Leaf (File) | `File.java` | +| Step 3 — The Composite (Directory) | `Directory.java` | +| Building and Using the Tree | `Main.java` | + +Run it: +```bash +javac 02-structural/composite/*.java -d out/composite +java -cp out/composite composite.Main +``` + ## Reference - *Design Patterns: Elements of Reusable Object-Oriented Software* — Gamma, Helm, Johnson, Vlissides