Read this as a website: https://williajm.github.io/java_patterns/
Mainstream design patterns you'll actually meet in real Java codebases, each demonstrated with a small, self-contained bookshop example. No academic filler: only patterns that working Java developers both encounter constantly (in the JDK, Spring, and friends) and hand-write in ordinary application code.
Every pattern has:
- a focused implementation under
src/main/java/com/bookshop/<category>/<pattern>/ - a
README.mdin the same package explaining the problem, the benefits, and where the pattern appears in the wild - a JUnit test written as executable documentation -- read the tests to see the pattern's payoff demonstrated, not just described
| Category | Pattern | Bookshop example | Key benefit |
|---|---|---|---|
| Creational | Builder | Assembling an Order with optional gift message, promo code, express delivery |
Readable construction of immutable objects; no telescoping constructors |
| Creational | Simple Factory | Creating card/PayPal/gift-card processors from a PaymentMethod |
Callers never couple to concrete classes; a new method is one compiler-checked change |
| Creational | Singleton | Shop-wide config and feature flags via the enum idiom | Guaranteed single shared instance -- with an honest "prefer DI" caveat |
| Structural | Adapter | Wrapping a legacy pence-and-status-codes payment gateway behind a clean interface | Incompatible APIs cooperate; the ugliness is quarantined in one class |
| Structural | Decorator | Stacking gift wrap, express handling and greeting card onto an order | Combine extras at runtime without 2^n subclasses |
| Structural | Facade | One checkout() call orchestrating inventory, payment and shipping |
The correct sequence is written once; callers can't get it wrong |
| Structural | Proxy | A caching stand-in for the distributor's slow remote catalog | Caching/laziness/access control added without touching callers |
| Behavioral | Strategy | Swappable discount rules: loyalty, bulk, seasonal sale | Pricing rules are configuration, not if/else ladders; each testable alone |
| Behavioral | Observer | Email/SMS/dashboard reactions to restocks | Event source is decoupled from its ever-growing list of reactions |
| Behavioral | Template Method | One sales-report skeleton, CSV and HTML variants | The algorithm exists once; formats can't drift apart |
| Behavioral | Chain of Responsibility | Order validation pipeline: stock -> address -> fraud | Checks are independent components; the pipeline is just a list |
The same bookshop, applied to design principles rather than patterns. Each package shows the good design in code and the violation as a snippet in its README, with tests demonstrating the payoff.
| Principle | Bookshop example | Key benefit |
|---|---|---|
| Single Responsibility | Receipt totals, printing and storage as three classes | A receipt redesign can't break the maths |
| Open/Closed | New shipping rates plug in; the quote calculator is never edited | New behaviour without re-risking tested code |
| Liskov Substitution | A free sample chapter is not a Purchasable, so it can't reach the till |
Contract violations become compile errors, not incidents |
| Interface Segregation | Bookseller/StockManager/Accountant roles instead of one fat staff interface |
No stubbed methods; till code can't call accounting |
| Dependency Inversion | OrderService depends on gateway/repository interfaces, details injected |
Business logic tested with fakes; infrastructure swappable |
| DRY | One authoritative Vat class; receipt and invoice cannot disagree |
A rule change is one edit; no silently-drifting copies |
Two docs that keep the rest of the repo honest:
- Anti-patterns -- the five failure modes you'll actually meet (God Class, magic numbers, copy-paste, global mutable state, Golden Hammer), each with its counter-example in this repo.
- Use with judgement -- none of these are rigid rules. Every pattern and principle here has a cost as well as a benefit, overuse is its own anti-pattern, and the page gives concrete signals for both over- and under-engineering.
Requires JDK 17+ and Maven.
mvn verify # lints (Checkstyle) and runs every pattern's testsThis is a curated list, not a complete catalogue: the bar for inclusion is "working Java developers hand-write this regularly". The rest of the Gang of Four is left out as a scope choice -- these patterns are real and still appear, just not often enough in ordinary application code to earn a chapter here:
- Command -- when the need is simply "a task as an object",
Runnableand lambdas usually carry it; the full pattern (queues, undo stacks, audit logs of operations) is genuinely useful but comparatively rare. - State -- real projects usually reach for an enum + switch or a state-machine library rather than the class-per-state shape.
- Composite -- common inside UI/tree libraries, rarely hand-written in application code.
- Iterator -- absorbed into the language:
Iterableand for-each. - Abstract Factory, Prototype, Bridge, Flyweight, Visitor, Mediator, Memento, Interpreter -- each has its niche (a DI container does much of Abstract Factory's everyday job; Visitor still shines in compilers and AST tooling), but in typical application code they're rare enough that a focused list serves you better than a complete one.
If you're deciding whether to use a pattern at all: the tests here show the problem each pattern earns its keep on. No matching problem, no pattern -- the simplest code that works wins. The full argument, including the signals of over- and under-engineering, is in Use with judgement.