Sync Composite to Java 25: H6 file labels, fix console output typo, README mapping

This commit is contained in:
2026-06-24 14:30:27 +05:30
parent 4091345b20
commit ad6a65a95d
6 changed files with 77 additions and 76 deletions

View File

@@ -1,53 +1,39 @@
package composite; package composite;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Composite — a directory that can hold both Files (leaves) * Composite — a directory that holds both Files (leaves) and other Directories.
* and other Directories (composites). *
* 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 — * The caller doesn't care whether a child is a File or Directory —
* both implement FileSystemItem and answer getSize(). * both answer getSize() and print() the same way.
*
* This is the power of Composite: uniform treatment of simple and complex.
*/ */
public class Directory implements FileSystemItem { public class Directory implements FileSystemItem {
private final String name; private final String name;
private final List<FileSystemItem> children = new ArrayList<>(); private final List<FileSystemItem> children = new ArrayList<>();
public Directory(String name) { this.name = name; }
public Directory(String name) { // Fluent add() — allows chaining: dir.add(file1).add(file2).add(subDir)
this.name = name;
}
public Directory add(FileSystemItem item) { public Directory add(FileSystemItem item) {
children.add(item); children.add(item);
return this; // fluent API for easy nesting return this;
} }
public void remove(FileSystemItem item) { children.remove(item); }
public void remove(FileSystemItem item) { @Override public String getName() { return name; }
children.remove(item);
}
@Override
public String getName() { return name; }
@Override @Override
public long getSize() { public long getSize() {
// Recursion: each child knows its own size. // Each child knows its own size: files return bytes, directories recurse.
// Files return their bytes; directories sum their children. // This is the Composite's core: uniform delegation down the tree.
return children.stream() return children.stream()
.mapToLong(FileSystemItem::getSize) .mapToLong(FileSystemItem::getSize)
.sum(); .sum();
} }
@Override @Override
public void print(String indent) { public void print(String indent) {
System.out.printf("%s[DIR] %s/ (%,d bytes total)%n", indent, name, getSize()); System.out.printf("%s[DIR] %s/ (%,d bytes total)%n", indent, name, getSize());
for (FileSystemItem child : children) { for (FileSystemItem child : children) {
child.print(indent + " "); // recurse with deeper indent child.print(indent + " "); // each level indents 4 more spaces
} }
} }
} }

View File

@@ -1,25 +1,18 @@
package composite; package composite;
/** /**
* Leaf — a single file. It has no children. * Leaf — a single file with no children.
* getSize() returns its own size. print() shows its name. * getSize() returns its own bytes. print() outputs a single line.
* * No knowledge of directories or nesting exists in this class.
* Notice: the File has no knowledge of directories or nesting.
* It just knows its own name and size.
*/ */
public class File implements FileSystemItem { public class File implements FileSystemItem {
private final String name; private final String name;
private final long size; private final long size;
public File(String name, long sizeBytes) { public File(String name, long sizeBytes) {
this.name = name; this.name = name;
this.size = sizeBytes; this.size = sizeBytes;
} }
@Override public String getName() { return name; } @Override public String getName() { return name; }
@Override public long getSize() { return size; } @Override public long getSize() { return size; }
@Override @Override
public void print(String indent) { public void print(String indent) {
System.out.printf("%s[FILE] %s (%,d bytes)%n", indent, name, size); System.out.printf("%s[FILE] %s (%,d bytes)%n", indent, name, size);

View File

@@ -1,12 +1,10 @@
package composite; package composite;
/** /**
* Component interface — the common contract for BOTH files (leaves) * Component — the common interface for both files (leaves) and directories (composites).
* and directories (composites). Clients work through this interface * Clients work through this interface and never need to know which type they're dealing with.
* and never need to know which they're dealing with.
*/ */
public interface FileSystemItem { public interface FileSystemItem {
String getName(); String getName();
long getSize(); // total size in bytes (recursive for directories) long getSize(); // total size in bytes recursive for directories
void print(String indent); // display the tree void print(String indent); // display the tree at the given indentation level
} }

View File

@@ -1,58 +1,36 @@
package composite; 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 class Main {
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("=== Composite Design Pattern Demo ===\n"); System.out.println("=== Composite Design Pattern Demo ===\n");
// Build the tree — mix files and directories freely
// Build a file system tree
Directory root = new Directory("project"); Directory root = new Directory("project");
Directory src = new Directory("src"); Directory src = new Directory("src");
Directory main = new Directory("main"); Directory main = new Directory("main");
main.add(new File("App.java", 4_200)) main.add(new File("App.java", 4_200))
.add(new File("Config.java", 1_800)) .add(new File("Config.java", 1_800))
.add(new File("Application.yml", 3_100)); .add(new File("Application.yml", 3_100));
Directory test = new Directory("test"); 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)); .add(new File("ConfigTest.java", 1_200));
src.add(main).add(test); src.add(main).add(test);
Directory resources = new Directory("resources"); 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("logback.xml", 900))
.add(new File("banner.txt", 200)); .add(new File("banner.txt", 200));
root.add(src) root.add(src)
.add(resources) .add(resources)
.add(new File("pom.xml", 8_400)) .add(new File("pom.xml", 8_400))
.add(new File("README.md", 2_100)); .add(new File("README.md", 2_100));
// Print the entire tree — recursion is automatic
// Print entire tree — recursion happens automatically
System.out.println("File system tree:"); System.out.println("File system tree:");
root.print(""); root.print("");
System.out.printf("%nTotal project size: %,d bytes%n", root.getSize()); System.out.printf("%nTotal project size: %,d bytes%n", root.getSize());
// The key demonstration: File and Directory through the same interface
// Client treats File and Directory identically
System.out.println("\n-- Treating File and Directory uniformly --"); System.out.println("\n-- Treating File and Directory uniformly --");
FileSystemItem[] items = { new File("standalone.txt", 500), src }; FileSystemItem[] items = { new File("standalone.txt", 500), src };
for (FileSystemItem item : items) { for (FileSystemItem item : items) {
System.out.printf("%s -> size: %,d bytes%n", item.getName(), item.getSize()); System.out.printf("%s -> size: %,d bytes%n", item.getName(), item.getSize());
} }
System.out.println("\n=== Demo complete ==="); System.out.println("\n=== Demo complete ===");
} }
} }

View File

@@ -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/

View File

@@ -215,6 +215,22 @@ javac 02-structural/bridge/*.java -d out/bridge
java -cp out/bridge bridge.Main 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 ## Reference
- *Design Patterns: Elements of Reusable Object-Oriented Software* — Gamma, Helm, Johnson, Vlissides - *Design Patterns: Elements of Reusable Object-Oriented Software* — Gamma, Helm, Johnson, Vlissides