Sync Decorator to Java 25: H6 file labels, fix Main.java JDK-output mismatch, README mapping
This commit is contained in:
@@ -1,35 +1,24 @@
|
|||||||
package decorator;
|
package decorator;
|
||||||
|
|
||||||
/**
|
|
||||||
* Decorator Design Pattern — Runnable Demo
|
|
||||||
*
|
|
||||||
* Shows how text processors can be stacked like Java IO streams.
|
|
||||||
* Each decorator adds one behaviour; the order of wrapping matters.
|
|
||||||
*
|
|
||||||
* Run: javac decorator/*.java && java decorator.Main
|
|
||||||
* Article: https://ankurm.com/decorator-design-pattern-java/
|
|
||||||
*/
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.out.println("=== Decorator Design Pattern Demo ===\n");
|
System.out.println("=== Decorator Design Pattern Demo ===\n");
|
||||||
|
|
||||||
String input = " hello world, badword is here ";
|
String input = " hello world, badword is here ";
|
||||||
System.out.println("Input: \"" + input + "\"");
|
System.out.println("Input: \"" + input + "\"\n");
|
||||||
System.out.println();
|
|
||||||
|
|
||||||
// Stack 1: just trim
|
// Stack 1: just trim
|
||||||
TextProcessor trimOnly = new TrimDecorator(new PlainTextProcessor());
|
TextProcessor trimOnly = new TrimDecorator(new PlainTextProcessor());
|
||||||
System.out.println("Trim only: \"" + trimOnly.process(input) + "\"");
|
System.out.println("Trim only: \"" + trimOnly.process(input) + "\"");
|
||||||
|
|
||||||
// Stack 2: trim, then upper case
|
// Stack 2: trim first (innermost), then uppercase (outermost)
|
||||||
|
// Execution order: PlainText → Trim → UpperCase
|
||||||
TextProcessor trimThenUpper =
|
TextProcessor trimThenUpper =
|
||||||
new UpperCaseDecorator(
|
new UpperCaseDecorator(
|
||||||
new TrimDecorator(
|
new TrimDecorator(
|
||||||
new PlainTextProcessor()));
|
new PlainTextProcessor()));
|
||||||
System.out.println("Trim + UpperCase: \"" + trimThenUpper.process(input) + "\"");
|
System.out.println("Trim + UpperCase: \"" + trimThenUpper.process(input) + "\"");
|
||||||
|
|
||||||
// Stack 3: trim, filter profanity, then upper case
|
// Stack 3: trim, filter profanity, then uppercase
|
||||||
TextProcessor full =
|
TextProcessor full =
|
||||||
new UpperCaseDecorator(
|
new UpperCaseDecorator(
|
||||||
new ProfanityFilterDecorator(
|
new ProfanityFilterDecorator(
|
||||||
@@ -37,15 +26,15 @@ public class Main {
|
|||||||
new PlainTextProcessor())));
|
new PlainTextProcessor())));
|
||||||
System.out.println("Trim + Filter + Upper: \"" + full.process(input) + "\"");
|
System.out.println("Trim + Filter + Upper: \"" + full.process(input) + "\"");
|
||||||
|
|
||||||
// Stack 4: different order — filter then trim (order matters!)
|
// Stack 4: filter THEN trim — different result because order changed
|
||||||
TextProcessor filterFirst =
|
TextProcessor filterFirst =
|
||||||
new TrimDecorator(
|
new TrimDecorator(
|
||||||
new ProfanityFilterDecorator(
|
new ProfanityFilterDecorator(
|
||||||
new PlainTextProcessor()));
|
new PlainTextProcessor()));
|
||||||
System.out.println("Filter + Trim: \"" + filterFirst.process(input) + "\"");
|
System.out.println("Filter + Trim: \"" + filterFirst.process(input) + "\"");
|
||||||
|
|
||||||
System.out.println();
|
System.out.println("\n--- JDK equivalent ---");
|
||||||
System.out.println("JDK parallel: new BufferedReader(new InputStreamReader(socket.getInputStream()))");
|
System.out.println("new BufferedReader(new InputStreamReader(socket.getInputStream()))");
|
||||||
System.out.println("Same pattern: each wrapper adds one behaviour, order matters.");
|
System.out.println("Same pattern: each wrapper adds one behaviour, order matters.");
|
||||||
|
|
||||||
System.out.println("\n=== Demo complete ===");
|
System.out.println("\n=== Demo complete ===");
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
package decorator;
|
package decorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concrete Component — the base object being decorated.
|
* Concrete Component — the starting point for decoration.
|
||||||
* Does nothing special: just returns the text as-is.
|
* Returns text unchanged. Decorators wrap around this
|
||||||
* All decorators wrap this (or other decorators on top of it).
|
* and transform the result one layer at a time.
|
||||||
*/
|
*/
|
||||||
public class PlainTextProcessor implements TextProcessor {
|
public class PlainTextProcessor implements TextProcessor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String process(String text) {
|
public String process(String text) {
|
||||||
return text; // base: no transformation
|
return text; // no transformation — base case
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
package decorator;
|
package decorator;
|
||||||
|
/** Concrete Decorator: replaces profane words with asterisks. */
|
||||||
/** Concrete Decorator 3: replaces bad words with asterisks. */
|
|
||||||
public class ProfanityFilterDecorator extends TextDecorator {
|
public class ProfanityFilterDecorator extends TextDecorator {
|
||||||
|
|
||||||
private static final String[] BAD_WORDS = {"badword", "spam"};
|
private static final String[] BAD_WORDS = {"badword", "spam"};
|
||||||
|
public ProfanityFilterDecorator(TextProcessor wrapped) { super(wrapped); }
|
||||||
public ProfanityFilterDecorator(TextProcessor wrapped) {
|
|
||||||
super(wrapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String process(String text) {
|
public String process(String text) {
|
||||||
String result = super.process(text);
|
String result = super.process(text);
|
||||||
|
|||||||
32
02-structural/decorator/README.md
Normal file
32
02-structural/decorator/README.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Decorator Design Pattern — Java Example
|
||||||
|
|
||||||
|
**Pattern:** Structural → Decorator
|
||||||
|
**Article:** https://ankurm.com/decorator-design-pattern-java/
|
||||||
|
|
||||||
|
## What this example shows
|
||||||
|
|
||||||
|
Builds a text-processing pipeline where behaviours (trim, uppercase, profanity filter) are stacked as wrapper objects around a `PlainTextProcessor`, all sharing the `TextProcessor` interface — the same idea behind `new BufferedReader(new InputStreamReader(...))` in the JDK.
|
||||||
|
|
||||||
|
## How to run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
javac decorator/*.java -d out/decorator
|
||||||
|
java -cp out/decorator decorator.Main
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires Java 25.
|
||||||
|
|
||||||
|
## Post Section ↔ File Mapping
|
||||||
|
|
||||||
|
| Post Section | File(s) |
|
||||||
|
|---|---|
|
||||||
|
| Step 1 — Component Interface | `TextProcessor.java` |
|
||||||
|
| Step 2 — Concrete Component | `PlainTextProcessor.java` |
|
||||||
|
| Step 3 — Base Decorator | `TextDecorator.java` |
|
||||||
|
| Step 4 — Concrete Decorators | `UpperCaseDecorator.java`, `TrimDecorator.java`, `ProfanityFilterDecorator.java` |
|
||||||
|
| Stacking the Decorators — Order Matters | `Main.java` |
|
||||||
|
|
||||||
|
Note: the JDK `InputStream`/`BufferedInputStream`/`GZIPInputStream` snippet under "Decorator in the JDK: I/O Streams" is illustrative only — it is not part of this repository's runnable example.
|
||||||
|
|
||||||
|
Article: https://ankurm.com/decorator-design-pattern-java/
|
||||||
|
All patterns: https://ankurm.com/design-patterns-java/
|
||||||
@@ -1,25 +1,22 @@
|
|||||||
package decorator;
|
package decorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Decorator — holds a reference to the wrapped TextProcessor.
|
* Base Decorator — holds a reference to the wrapped TextProcessor
|
||||||
* All concrete decorators extend this instead of implementing
|
* and delegates to it. Concrete decorators extend this class.
|
||||||
* TextProcessor directly. This avoids repeating the delegation
|
|
||||||
* boilerplate in every decorator.
|
|
||||||
*
|
*
|
||||||
* Crucially: it delegates to the wrapped processor first,
|
* Why an abstract class rather than another interface implementation?
|
||||||
* then applies its own transformation to the result.
|
* To avoid repeating the wrapping boilerplate (the field + constructor +
|
||||||
|
* delegation call) in every single concrete decorator.
|
||||||
*/
|
*/
|
||||||
public abstract class TextDecorator implements TextProcessor {
|
public abstract class TextDecorator implements TextProcessor {
|
||||||
|
|
||||||
protected final TextProcessor wrapped;
|
protected final TextProcessor wrapped;
|
||||||
|
|
||||||
protected TextDecorator(TextProcessor wrapped) {
|
protected TextDecorator(TextProcessor wrapped) {
|
||||||
this.wrapped = wrapped;
|
this.wrapped = wrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String process(String text) {
|
public String process(String text) {
|
||||||
// Delegate to the wrapped processor first, then let subclass apply its transform
|
// Default: pass through to the wrapped processor.
|
||||||
|
// Concrete decorators call super.process(text) to invoke this,
|
||||||
|
// then apply their own transformation to the result.
|
||||||
return wrapped.process(text);
|
return wrapped.process(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package decorator;
|
package decorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component interface — defines what all text processors do.
|
* Component — defines what all text processors do.
|
||||||
* Both the concrete processor AND all decorators implement this.
|
* Both the base implementation AND every decorator implement this.
|
||||||
* This is what makes them stackable.
|
* Sharing this type is what makes decorators stackable.
|
||||||
*/
|
*/
|
||||||
public interface TextProcessor {
|
public interface TextProcessor {
|
||||||
String process(String text);
|
String process(String text);
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
package decorator;
|
package decorator;
|
||||||
|
/** Concrete Decorator: trims leading and trailing whitespace. */
|
||||||
/** Concrete Decorator 2: trims leading/trailing whitespace. */
|
|
||||||
public class TrimDecorator extends TextDecorator {
|
public class TrimDecorator extends TextDecorator {
|
||||||
|
public TrimDecorator(TextProcessor wrapped) { super(wrapped); }
|
||||||
public TrimDecorator(TextProcessor wrapped) {
|
|
||||||
super(wrapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String process(String text) {
|
public String process(String text) {
|
||||||
return super.process(text).trim();
|
return super.process(text).trim();
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
package decorator;
|
package decorator;
|
||||||
|
/** Concrete Decorator: converts result to upper case. */
|
||||||
/** Concrete Decorator 1: converts all text to upper case. */
|
|
||||||
public class UpperCaseDecorator extends TextDecorator {
|
public class UpperCaseDecorator extends TextDecorator {
|
||||||
|
|
||||||
public UpperCaseDecorator(TextProcessor wrapped) {
|
public UpperCaseDecorator(TextProcessor wrapped) {
|
||||||
super(wrapped);
|
super(wrapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String process(String text) {
|
public String process(String text) {
|
||||||
// Get the result from whatever is below us in the stack,
|
// 1. Get result from everything below in the stack
|
||||||
// then apply OUR transformation on top of it.
|
// 2. Apply OUR transformation: uppercase
|
||||||
return super.process(text).toUpperCase();
|
return super.process(text).toUpperCase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -231,6 +231,24 @@ javac 02-structural/composite/*.java -d out/composite
|
|||||||
java -cp out/composite composite.Main
|
java -cp out/composite composite.Main
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Decorator (`02-structural/decorator/`)
|
||||||
|
|
||||||
|
| Post Section | File(s) |
|
||||||
|
|---|---|
|
||||||
|
| Step 1 — Component Interface | `TextProcessor.java` |
|
||||||
|
| Step 2 — Concrete Component | `PlainTextProcessor.java` |
|
||||||
|
| Step 3 — Base Decorator | `TextDecorator.java` |
|
||||||
|
| Step 4 — Concrete Decorators | `UpperCaseDecorator.java`, `TrimDecorator.java`, `ProfanityFilterDecorator.java` |
|
||||||
|
| Stacking the Decorators — Order Matters | `Main.java` |
|
||||||
|
|
||||||
|
Note: the JDK `InputStream`/`BufferedInputStream`/`GZIPInputStream` snippet is illustrative only — it is not part of this repository's runnable example.
|
||||||
|
|
||||||
|
Run it:
|
||||||
|
```bash
|
||||||
|
javac 02-structural/decorator/*.java -d out/decorator
|
||||||
|
java -cp out/decorator decorator.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
|
||||||
|
|||||||
Reference in New Issue
Block a user