16 KiB
GoF Design Patterns in Java
Complete runnable Java 25 implementations of all 23 Gang of Four design patterns. Each pattern has a dedicated article at ankurm.com with a UML diagram, step-by-step explanation, and console output.
Structure
design-patterns/
├── 01-creational/
│ ├── factory-method/
│ ├── abstract-factory/
│ ├── builder/
│ ├── prototype/
│ └── singleton/
├── 02-structural/
│ ├── adapter/
│ ├── bridge/
│ ├── composite/
│ ├── decorator/
│ ├── facade/
│ ├── flyweight/
│ └── proxy/
└── 03-behavioral/
├── chain-of-responsibility/
├── command/
├── interpreter/
├── iterator/
├── mediator/
├── memento/
├── observer/
├── state/
├── strategy/
├── template-method/
└── visitor/
Prerequisites
- Java 25+ (Eclipse Temurin recommended)
- No build tool required — plain
javac/java
Run any pattern
# Linux / macOS / Git Bash (Windows)
javac 03-behavioral/strategy/*.java -d out/strategy
java -cp out/strategy strategy.Main
REM Windows Command Prompt
javac 03-behavioral\strategy\*.java -d out\strategy
java -cp out\strategy strategy.Main
Articles
Post ↔ Code Mapping
Each article is split into multiple runnable parts. This section maps every post section to the exact file(s) in this repository, so you can jump straight from "what I just read" to "the file that implements it." Updated pattern-by-pattern as each article is verified against Java 25.
Factory Method (01-creational/factory-method/)
| Post Section | File(s) |
|---|---|
| The Problem: Object Creation Leaks Into Business Logic | NotificationService.java (anti-pattern shown for contrast — not called by Main) |
| Part 1 — The Notification Interface | Notification.java |
| Part 2 — The Concrete Notification Classes | EmailNotification.java, SmsNotification.java, PushNotification.java |
| Part 3 — The NotificationSender Class | NotificationSender.java |
| Part 4 — The Concrete Sender Subclasses | EmailSender.java, SmsSender.java, PushSender.java |
| Part 5 — Running It: Client Code and Output | Main.java |
| Adding a New Channel (Slack) | SlackNotification.java, SlackSender.java |
Run it:
javac 01-creational/factory-method/*.java -d out/factory-method
java -cp out/factory-method factorymethod.Main
Abstract Factory (01-creational/abstract-factory/)
| Post Section | File(s) |
|---|---|
| Part 1 — The Abstract Products: Button and TextField | Button.java, TextField.java |
| Part 2 — The Concrete Products (Light family) | LightButton.java, LightTextField.java |
| Part 2 — The Concrete Products (Dark family) | DarkButton.java, DarkTextField.java |
| Part 3 — The Abstract Factory: UIFactory Interface | UIFactory.java |
| Part 4 — The Concrete Factories | LightThemeFactory.java, DarkThemeFactory.java |
| Part 5 — The Client: Application | Application.java, Main.java |
Run it:
javac 01-creational/abstract-factory/*.java -d out/abstract-factory
java -cp out/abstract-factory abstractfactory.Main
# or, for the dark theme family:
java -Dapp.theme=dark -cp out/abstract-factory abstractfactory.Main
Builder (01-creational/builder/)
| Post Section | File(s) |
|---|---|
| Part 1 — The Product: HttpRequest (Immutable) | HttpRequest.java |
| Part 2 — The Director: Encapsulating Common Configurations | HttpClientConfig.java |
| Part 3 — Using the Builder: Client Code | Main.java |
Note: the Lombok @Builder example and the JDK standard-library snippets (HttpRequest.newBuilder(), StringBuilder, ProcessBuilder) are illustrative only — they are not part of this repository's runnable example.
Run it:
javac 01-creational/builder/*.java -d out/builder
java -cp out/builder builder.Main
Prototype (01-creational/prototype/)
| 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.
Run it:
javac 01-creational/prototype/*.java -d out/prototype
java -cp out/prototype prototype.Main
Singleton (01-creational/singleton/)
| Post Section | File(s) |
|---|---|
| Implementation 1 — Eager Initialization | AppConfigEager.java |
| Implementation 2 — Naïve Lazy Initialization | AppConfigNaiveLazy.java |
| Implementation 3 — Synchronized Method | AppConfigSynchronized.java |
| Implementation 4 — Double-Checked Locking | AppConfigDCL.java |
| Implementation 5 — Initialization-on-Demand Holder | AppConfigHolder.java |
| Implementation 6 — Enum Singleton | AppConfigEnum.java |
| Running All Six Implementations Together | Main.java |
Each implementation is renamed to its own class (e.g. AppConfigEager, AppConfigHolder) so all six can be compiled together in one package and compared directly. Main.java exercises all six in one run. The "Breaking via Reflection," "Breaking via Serialization," and Spring @Component snippets are illustrative only — they are not part of this repository's runnable example.
Run it:
javac 01-creational/singleton/*.java -d out/singleton
java -cp out/singleton singleton.Main
Adapter (02-structural/adapter/)
| Post Section | File(s) |
|---|---|
| Step 1 — Define the Target Interface | PaymentGateway.java |
| Step 2 — The Adaptee (What You're Wrapping) | StripeClient.java |
| Step 3 — The Object Adapter | StripePaymentAdapter.java |
| Step 4 — The Client (Knows Nothing About Stripe) | OrderService.java |
| Putting It Together: The Main Demo | Main.java |
| Object Adapter vs Class Adapter | StripePaymentClassAdapter.java |
Note: the InputStreamReader JDK-adapter snippet is illustrative only — it is not part of this repository's runnable example.
Run it:
javac 02-structural/adapter/*.java -d out/adapter
java -cp out/adapter adapter.Main
Bridge (02-structural/bridge/)
| Post Section | File(s) |
|---|---|
| Step 1 — The Implementor Interface | Device.java |
| Step 2 — Concrete Implementors (TV and Radio) | TV.java, Radio.java |
| Step 3 — The Abstraction (RemoteControl) | RemoteControl.java |
| Step 4 — Refined Abstraction (AdvancedRemote) | AdvancedRemote.java |
| Wiring It Together: Main Demo | Main.java |
Note: the "Class Explosion Problem — Visualised" pseudocode and the JDBC/SLF4J snippets are illustrative only — they are not part of this repository's runnable example.
Run it:
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:
javac 02-structural/composite/*.java -d out/composite
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:
javac 02-structural/decorator/*.java -d out/decorator
java -cp out/decorator decorator.Main
Facade (02-structural/facade/)
| Post Section | File(s) |
|---|---|
| The Subsystem (Complex, But Unchanged) — VideoFile | VideoFile.java |
| The Subsystem (Complex, But Unchanged) — Codec interface | Codec.java |
| The Subsystem (Complex, But Unchanged) — Concrete Codecs | MPEG4CompressionCodec.java, OggCompressionCodec.java |
| The Subsystem (Complex, But Unchanged) — CodecFactory | CodecFactory.java |
| The Subsystem (Complex, But Unchanged) — BitrateReader | BitrateReader.java |
| The Subsystem (Complex, But Unchanged) — AudioMixer | AudioMixer.java |
| The Facade | VideoConversionFacade.java |
| Client Code: One Line | Main.java |
Note: the SLF4J LoggerFactory, JDBC DriverManager, and Spring JdbcTemplate snippets under "Facade in the JDK and Real Frameworks" are illustrative only — they are not part of this repository's runnable example.
Run it:
javac 02-structural/facade/*.java -d out/facade
java -cp out/facade facade.Main
Flyweight (02-structural/flyweight/)
| Post Section | File(s) |
|---|---|
| Step 1 — The Flyweight (TreeType) | TreeType.java |
| Step 2 — The Factory (TreeFactory) | TreeFactory.java |
| Step 3 — The Context (Tree) | Tree.java |
| Putting the Forest Together | Main.java |
Note: the "Flyweight in the JDK: String Pool and Integer Cache" and "Measuring the Benefit" snippets are illustrative only — they are not part of this repository's runnable example.
Run it:
javac 02-structural/flyweight/*.java -d out/flyweight
java -cp out/flyweight flyweight.Main
Proxy (02-structural/proxy/)
| Post Section | File(s) |
|---|---|
| The Subject Interface | DatabaseConnection.java |
| The Real Subject | RealDatabaseConnection.java |
| Virtual Proxy: Lazy Initialisation | LazyConnectionProxy.java |
| Logging Proxy: Cross-Cutting Concerns | LoggingProxy.java |
| Wiring It Together: Proxy Chaining | Main.java |
Note: the "Dynamic Proxy with java.lang.reflect.Proxy" snippet is illustrative only — it is not part of this repository's runnable example.
Run it:
javac 02-structural/proxy/*.java -d out/proxy
java -cp out/proxy proxy.Main
Chain of Responsibility (03-behavioral/chain-of-responsibility/)
| Post Section | File(s) |
|---|---|
| The Problem: Rigid Routing Logic | illustrative only — not part of this repository's runnable example |
| Implementation: Support Ticket Escalation — base handler | SupportHandler.java |
| Implementation: Support Ticket Escalation — the ticket | SupportTicket.java |
| Each concrete handler checks only its own responsibility — Level 1 & 2 | Level1Support.java, Level2Support.java |
| Level3Support and CriticalIncidentTeam follow exactly the same shape | Level3Support.java, CriticalIncidentTeam.java |
| The chain is assembled in one place | Main.java |
Run it:
javac 03-behavioral/chain-of-responsibility/*.java -d out/chain
java -cp out/chain chain.Main
Command (03-behavioral/command/)
| Post Section | File(s) |
|---|---|
| Step 1 — Command Interface | Command.java |
| Step 2 — The Receiver (TextEditor) | TextEditor.java |
| Step 3 — Concrete Commands | InsertCommand.java, DeleteCommand.java |
| Step 4 — The Invoker (CommandHistory) | CommandHistory.java |
| Demo | Main.java |
Run it:
javac 03-behavioral/command/*.java -d out/command
java -cp out/command command.Main
Iterator (03-behavioral/iterator/)
| Post Section | File(s) |
|---|---|
| Custom Iterator Implementation — the Iterator interface | BookIterator.java |
| Custom Iterator Implementation — the element type | Book.java |
| Custom Iterator Implementation — the Aggregate (BookShelf, ForwardIterator, DecadeIterator) | BookShelf.java |
| Custom Iterator Implementation — wiring it together | Main.java |
Note: the "How Java's For-Each Loop Uses Iterator" snippet (Iterable<Book> example) is illustrative only — it is not part of this repository's runnable example.
Run it:
javac 03-behavioral/iterator/*.java -d out/iterator
java -cp out/iterator iterator.Main
Mediator (03-behavioral/mediator/)
| Post Section | File(s) |
|---|---|
| Implementation: Chat Room — the Mediator interface | ChatMediator.java |
| Implementation: Chat Room — the Colleague | User.java |
| Implementation: Chat Room — the Concrete Mediator | ChatRoom.java |
| Implementation: Chat Room — wiring it together | Main.java |
Note: the "Mediator in Practice: MVC and Spring Events" snippet (ApplicationEventPublisher/@EventListener example) is illustrative only — it is not part of this repository's runnable example.
Run it:
javac 03-behavioral/mediator/*.java -d out/mediator
java -cp out/mediator mediator.Main
Memento (03-behavioral/memento/)
| Post Section | File(s) |
|---|---|
| The Memento: Immutable and Opaque | EditorMemento.java |
| The Originator (Editor) | Editor.java |
| The Caretaker (History) | History.java |
| Wiring it together (Main demo) | Main.java |
Note: the "Serialization as Memento" snippet (ObjectOutputStream/ObjectInputStream example) is illustrative only — it is not part of this repository's runnable example.
Run it:
javac 03-behavioral/memento/*.java -d out/memento
java -cp out/memento memento.Main
Reference
- Design Patterns: Elements of Reusable Object-Oriented Software — Gamma, Helm, Johnson, Vlissides
- https://refactoring.guru/design-patterns