Design patterns basics: 50 cards on SOLID and the Gang of Four
SOLID, DRY, KISS and YAGNI, plus the Gang of Four patterns used most: singleton, factory, builder, adapter, decorator, observer, strategy, command and more.
Practice now
Study without an account
Scheduled with the default Askesis timings. Progress is saved in this browser.
All 50 cards
- 1What does SOLID stand for?Single responsibility, Open/closed, Liskov substitution, Interface segregation, Dependency inversionFive object-oriented design principles popularized by Robert C. Martin.
- 2Single responsibility principleA class or module should have one reason to change: one job, serving one part of the businessAn
Invoiceclass that calculates totals, renders PDFs and sends emails has three reasons to change. - 3Open/closed principleCode should be open for extension but closed for modification: add behavior by adding code, such as a new class or strategy, instead of editing code that worksA
switchon a type that you edit for every new case is the classic violation. - 4Liskov substitution principleObjects of a subtype must work anywhere the base type is expected, without surprising the callerClassic violation:
Square extends Rectangle. Setting a square’s width also changes its height, which breaks code that expects a rectangle’s sides to vary independently. - 5Interface segregation principleClients shouldn’t depend on methods they don’t use, so prefer several small, focused interfaces over one large oneA
Printershouldn’t have to implementfax()just because it shares aMachineinterface with a fax machine. - 6Dependency inversion principleHigh-level code should depend on abstractions, not on concrete low-level details; the details implement those abstractionsAn
OrderServicedepends on aPaymentGatewayinterface, not directly on aStripeClient. - 7DRY“Don’t repeat yourself”: every piece of knowledge should have a single, authoritative representation in the systemIt’s about duplicated knowledge, not code that merely looks similar. Merging look-alike code that changes for different reasons creates the wrong abstraction.
- 8KISS“Keep it simple, stupid”: prefer the simplest design that solves the problem
- 9YAGNI“You aren’t gonna need it”: don’t build features or flexibility until they’re actually neededFrom Extreme Programming. Speculative generality costs time now and complexity forever.
- 10Composition over inheritanceBuild behavior by combining objects that each do one thing (has-a) instead of extending class hierarchies (is-a)Composed parts can be swapped at runtime, and you avoid fragile base classes and deep hierarchies.
- 11Law of DemeterA method should talk only to its immediate collaborators, not reach through them: avoid chains like
order.getCustomer().getAddress().getCity()Also called the principle of least knowledge. - 12Coupling vs cohesionCoupling is how much modules depend on each other; cohesion is how closely the parts of one module belong together. Aim for low coupling and high cohesion
- 13What is a design pattern?A named, reusable solution to a problem that keeps recurring in software design: a template to adapt, not finished code
- 14The three categories of Gang of Four patternsCreational (how objects are created), structural (how objects are composed), behavioral (how objects interact and share responsibilities)From “Design Patterns” (1994) by Gamma, Helm, Johnson and Vlissides, which catalogs 23 patterns.
- 15SingletonEnsures a class has only one instance and provides a global point of access to itOften considered an anti-pattern: it’s global state that hides dependencies and makes testing hard. Creating one instance and passing it in is usually better.
- 16Which pattern is this?typescript
class Config { private static instance?: Config private constructor() {} static get(): Config { return (Config.instance ??= new Config()) } }Singleton: the private constructor blocksnew Config()from outside, andConfig.get()always returns the same instance - 17Factory methodCreates objects through a method instead of direct constructor calls, so a subclass or configuration decides which concrete class to instantiateCallers depend only on the product’s interface, not on the concrete classes.
- 18Abstract factoryAn interface for creating families of related objects without naming their concrete classesA
UIFactorywithcreateButton()andcreateCheckbox(), implemented once for Windows and once for macOS. - 19BuilderConstructs a complex object step by step, often through a fluent API that ends with
build()javaHttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://example.com")) .header("Accept", "application/json") .GET() .build(); - 20PrototypeCreates new objects by copying an existing instance instead of building them from scratchUseful when objects are expensive to set up, or when you don’t know their concrete class. In JavaScript,
structuredClone(obj)makes a deep copy. - 21AdapterWraps an object to convert its interface into the one clients expect, so classes with incompatible interfaces can work togetherLike a travel plug adapter: the same electricity through a different shape.
- 22DecoratorWraps an object in another object with the same interface that adds behavior, without changing the original class; wrappers can be stackedJava I/O is built this way:
new BufferedReader(new FileReader("data.txt"))adds buffering to anyReader. - 23FacadeA simple interface in front of a complex subsystem, e.g. one
VideoConverter.convert(file, "mp4")call that hides codecs, buffers and containersClients can still use the subsystem directly when they need more control. - 24ProxyA stand-in with the same interface as the real object that controls access to it: lazy loading, permission checks, caching or calling a remote serviceJavaScript’s built-in
Proxylets you intercept property access on any object. - 25CompositeComposes objects into tree structures so clients can treat single items and groups of items the same wayA UI
Panelholds buttons and other panels; callingrender()on a panel renders all of its children. - 26Adapter vs decorator vs proxy vs facadeAn adapter changes an interface; a decorator keeps it and adds behavior; a proxy keeps it and controls access; a facade puts one simpler interface over many
- 27ObserverA subject keeps a list of observers and notifies all of them when its state changes (one-to-many)typescript
// DOM events are observers: the button notifies both listeners button.addEventListener("click", () => save()) button.addEventListener("click", () => track("save")) - 28StrategyDefines a family of interchangeable algorithms behind one interface, so the algorithm can be chosen at runtimeWith first-class functions, a strategy is often just a function, like the comparator in
items.sort((a, b) => a.price - b.price). - 29CommandWraps a request, the action and its parameters, in an object so it can be queued, logged, retried or undone
- 30Template methodA base class defines the skeleton of an algorithm and lets subclasses override specific steps without changing its overall structure
- 31IteratorGives sequential access to the elements of a collection without exposing how it’s storedBuilt into most languages:
for...ofwithSymbol.iteratorin JavaScript,__iter__and__next__in Python. - 32StateAn object delegates its behavior to a state object and swaps that object when its state changes, so it seems to change classIt replaces large
switchstatements on a status field, e.g. an order that is pending, paid, shipped or cancelled. - 33Chain of responsibilityPasses a request along a chain of handlers; each one either handles it or passes it on to the nextWeb framework middleware for authentication, logging and rate limiting works this way.
- 34Strategy vs stateBoth delegate to swappable objects. With strategy, the client picks the algorithm and the strategies don’t know each other; with state, the states themselves switch to the next state as things change
- 35Dependency injectionAn object receives its dependencies from outside, usually through its constructor, instead of creating them itselftypescript
class OrderService { constructor(private payments: PaymentGateway) {} } new OrderService(new StripeGateway()) // production new OrderService(new FakeGateway()) // tests - 36Dependency inversion vs dependency injection vs IoC containerDependency inversion is the principle (depend on abstractions); dependency injection is the technique (pass dependencies in); an IoC container is a library that wires dependencies together automatically
- 37Why is a service locator often called an anti-pattern?Classes pull their dependencies from a global registry (
Locator.get(Mailer)), so the dependencies are hidden instead of listed in the constructor, which makes them harder to see, swap and test - 38God objectAn anti-pattern where one class knows or does far too much and attracts every unrelated changeSplit it along its responsibilities (single responsibility principle).
- 39When shouldn’t you use a design pattern?When the problem it solves isn’t there yet. Patterns add indirection, and forcing them in makes simple code harder to readStart simple, and refactor toward a pattern when duplication or change actually calls for it.
- 40When an order is placed, email, SMS and analytics must react, without the order code knowing about themPick a patternObserver (or publish-subscribe): the order publishes an event, and each service subscribes to it
- 41A constructor has a dozen optional parametersPick a patternBuilder: set only the options you need, then call
build() - 42Your code expects aPick a pattern
PaymentGatewayinterface, but the vendor’s SDK has different method namesAdapter: wrap the SDK in a class that implementsPaymentGateway - 43Add caching and logging around a repository without changing its classPick a patternDecorator: wrap the repository in objects with the same interface, each adding one behavior
- 44Shipping cost is calculated differently for each carrier, chosen at checkoutPick a patternStrategy: one
ShippingCalculatorinterface, with one implementation per carrier - 45A text editor needs undo and redoPick a patternCommand: each edit is an object with
execute()andundo(), kept on undo and redo stacks - 46Give callers onePick a pattern
placeOrder()method that coordinates the inventory, payment and shipping subsystemsFacade: a single entry point that hides how the subsystems work together - 47Create aPick a pattern
CsvParser,JsonParserorXmlParserbased on a file extension known only at runtimeFactory: acreateParser(ext)method returns the right implementation behind a commonParserinterface - 48Load a large image from disk only when it’s first displayedPick a patternProxy (a virtual proxy): a lightweight stand-in loads the real image on first use
- 49A vending machine behaves differently when idle, when holding coins and when sold outPick a patternState: one object per state, each handling input and switching the machine to the next state
- 50Folders contain files and other folders, and you need the total size of any of themPick a patternComposite:
FileandFoldershare asize()method, and a folder sums the sizes of its children
Frequently asked questions
Do I need to know all 23 Gang of Four patterns?
No. About a dozen, such as factory, builder, adapter, decorator, facade, observer, strategy and command, cover most real code. The rest are worth recognizing when you meet them.
Which language are the examples in?
TypeScript, with a couple of Java snippets. The patterns are language-independent, and many get simpler in languages with first-class functions.
Is the singleton pattern bad?
Not always, but it is global state in disguise: it hides dependencies and complicates tests. Creating one instance at startup and passing it where it is needed gives the same benefit without the drawbacks.
More in Computer science
Computer science
CS fundamentals: binary and hex, two’s complement, Big-O, stack vs heap, threads and deadlocks, CPU caches, compilers, UTF-8 and floating point.
50 cardsMorse code
Learn International Morse code: all 26 letters, the 10 digits, common punctuation and signals like SOS, each with its rhythm and a memory aid.
50 cardsDS & Algorithms
Big-O for arrays, hash maps, heaps, trees and graphs, sorting trade-offs, and the patterns behind interview problems: two pointers, sliding windows, BFS, Dijkstra and DP.
50 cardsSoftware testing
Software testing fundamentals: the test pyramid, unit, integration and E2E tests, TDD, mocks vs stubs vs fakes, coverage, mutation and property-based testing.
50 cardsAPI design
API design fundamentals: REST, HTTP methods, status codes, pagination, versioning, OAuth 2.0 and JWT, CORS, OpenAPI, GraphQL, gRPC and webhooks.
50 cardsSystem design
System design fundamentals: load balancers, caching, CDNs, replication and sharding, CAP, consistency, queues, rate limiting and back-of-the-envelope math.
50 cardsCryptography
Cryptography flashcards: AES modes, RSA, elliptic curves, Diffie–Hellman, hashing, HMAC, signatures, password hashing, TLS 1.3 and common pitfalls.
50 cards