1"use strict";(globalThis.webpackChunkdev_mountain=globalThis.webpackChunkdev_mountain||[]).push([[5299],{28453(e,n,t){t.d(n,{R:()=>o,x:()=>a});var s=t(96540);const r={},i=s.createContext(r);function o(e){const n=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),s.createElement(i.Provider,{value:n},e.children)}},52879(e,n,t){t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>o,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"cs-concepts/design-patterns","title":"Design Patterns","description":"Personal reference notes. Primary source: refactoring.guru/design-patterns \u2014 go there for animations and deeper examples. This page exists so that \\"I read about this once\\" turns into \\"I actually know this.\\"","source":"@site/resources/cs-concepts/design-patterns.md","sourceDirName":"cs-concepts","slug":"/cs-concepts/design-patterns","permalink":"/resources/cs-concepts/design-patterns","draft":false,"unlisted":false,"editUrl":"https://github.com/AbdulDevHub/Docusaurus/tree/main/resources/cs-concepts/design-patterns.md","tags":[{"inline":true,"label":"software-design","permalink":"/resources/tags/software-design"},{"inline":true,"label":"oop","permalink":"/resources/tags/oop"},{"inline":true,"label":"architecture","permalink":"/resources/tags/architecture"},{"inline":true,"label":"reference","permalink":"/resources/tags/reference"}],"version":"current","sidebarPosition":1,"frontMatter":{"id":"design-patterns","title":"Design Patterns","sidebar_label":"Design Patterns","sidebar_position":1,"tags":["software-design","oop","architecture","reference"]},"sidebar":"resourcesSidebar","previous":{"title":"Regex (JavaScript)","permalink":"/resources/cs-concepts/regex-cheatsheet"},"next":{"title":"System Design Fundamentals","permalink":"/resources/system-design/system-design-fundamentals"}}');var r=t(74848),i=t(28453);const o={id:"design-patterns",title:"Design Patterns",sidebar_label:"Design Patterns",sidebar_position:1,tags:["software-design","oop","architecture","reference"]},a=void 0,l={},c=[{value:"Why bother with these at all",id:"why-bother-with-these-at-all",level:2},{value:"The three families",id:"the-three-families",level:2},{value:"Creational Patterns",id:"creational-patterns",level:2},{value:"Singleton",id:"singleton",level:3},{value:"Factory Method",id:"factory-method",level:3},{value:"Builder",id:"builder",level:3},{value:"Structural Patterns",id:"structural-patterns",level:2},{value:"Adapter",id:"adapter",level:3},{value:"Decorator",id:"decorator",level:3},{value:"Facade",id:"facade",level:3},{value:"Proxy",id:"proxy",level:3},{value:"Behavioral Patterns",id:"behavioral-patterns",level:2},{value:"Strategy",id:"strategy",level:3},{value:"Observer",id:"observer",level:3},{value:"Command",id:"command",level:3},{value:"The rest (know the name, look up the details when you need them)",id:"the-rest-know-the-name-look-up-the-details-when-you-need-them",level:2},{value:"Creational",id:"creational",level:3},{value:"Structural",id:"structural",level:3},{value:"Behavioral",id:"behavioral",level:3},{value:"Practical takeaways",id:"practical-takeaways",level:2}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["Personal reference notes. Primary source: ",(0,r.jsx)(n.a,{href:"https://refactoring.guru/design-patterns",children:"refactoring.guru/design-patterns"}),' \u2014 go there for animations and deeper examples. This page exists so that "I read about this once" turns into "I actually know this."']}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"why-bother-with-these-at-all",children:"Why bother with these at all"}),"\n",(0,r.jsxs)(n.p,{children:["For a long time my honest opinion was: ",(0,r.jsx)(n.em,{children:"this feels like academic overhead for problems I don't have."})," That's not entirely wrong \u2014 a lot of pattern usage in the wild is over-engineering, applied because someone read a book, not because the code needed it."]}),"\n",(0,r.jsx)(n.p,{children:"The reframe that actually made these click for me:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["A design pattern is ",(0,r.jsx)(n.strong,{children:"not"})," a piece of code you copy-paste. It's a ",(0,r.jsx)(n.em,{children:"named solution shape"}),' for a recurring problem \u2014 a vocabulary word for "oh, this is one of those situations."']}),"\n",(0,r.jsxs)(n.li,{children:["The value isn't in ",(0,r.jsx)(n.em,{children:"implementing"})," the pattern from memory. The value is in ",(0,r.jsx)(n.strong,{children:"recognizing the shape of the problem"})," so you don't reinvent (badly) something well understood."]}),"\n",(0,r.jsx)(n.li,{children:"Most of the time you'll implement a rough, informal version without even naming it. That's fine \u2014 that's the pattern working."}),"\n",(0,r.jsxs)(n.li,{children:["You should be more suspicious of a codebase with patterns ",(0,r.jsx)(n.em,{children:"forced"})," into everything than one with none at all. Patterns solve specific problems; if the problem isn't there, the pattern is just ceremony."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Rule of thumb:"})," if you can explain ",(0,r.jsx)(n.em,{children:"why"})," you need the flexibility a pattern provides (not just \"it's more OOP-correct\"), use it. If not, don't."]}),"\n",(0,r.jsx)(n.h2,{id:"the-three-families",children:"The three families"}),"\n",(0,r.jsx)(n.p,{children:'Design patterns (the classic "Gang of Four" set) split into three categories by what they\'re solving:'}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Category"}),(0,r.jsx)(n.th,{children:"Question it answers"}),(0,r.jsx)(n.th,{children:"Examples"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"Creational"})}),(0,r.jsx)(n.td,{children:"How do I create objects flexibly, without hardcoding the exact class?"}),(0,r.jsx)(n.td,{children:"Singleton, Factory Method, Builder"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"Structural"})}),(0,r.jsx)(n.td,{children:"How do I compose objects/classes into larger structures cleanly?"}),(0,r.jsx)(n.td,{children:"Adapter, Decorator, Facade"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.strong,{children:"Behavioral"})}),(0,r.jsx)(n.td,{children:"How do objects communicate and assign responsibility for behavior?"}),(0,r.jsx)(n.td,{children:"Strategy, Observer, Command"})]})]})]}),"\n",(0,r.jsx)(n.p,{children:"Below: detailed notes on the patterns I actually run into. Everything else is listed at the bottom so I at least recognize the name and know when to go look it up."}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h2,{id:"creational-patterns",children:"Creational Patterns"}),"\n",(0,r.jsx)(n.h3,{id:"singleton",children:"Singleton"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"})," You need exactly one instance of a class to exist (e.g. a config manager, a connection pool, a logger), and you need a single well-known global access point to it."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"The catch:"})," Singleton is the most ",(0,r.jsx)(n.em,{children:"overused"})," and most ",(0,r.jsx)(n.em,{children:"criticized"})," pattern on this list. It's basically a global variable with a fancy name. It makes testing harder (hidden shared state) and creates hidden coupling. Use it deliberately, not by default."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:'class AppConfig {\n private static instance: AppConfig;\n private constructor(public readonly env: string) {}\n\n static getInstance(): AppConfig {\n if (!AppConfig.instance) {\n AppConfig.instance = new AppConfig(process.env.NODE_ENV ?? "development");\n }\n return AppConfig.instance;\n }\n}\n\nconst config = AppConfig.getInstance();\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"})," Truly singular, stateless-ish resources \u2014 logging, a single DB connection pool, a cache client. In modern practice this is often replaced by ",(0,r.jsx)(n.strong,{children:"dependency injection"}),' (pass the single instance in explicitly instead of reaching for a global), which keeps the "one instance" guarantee without the hidden-global downside.']}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h3,{id:"factory-method",children:"Factory Method"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"})," You have a base class/interface, and the exact subclass to instantiate should be decided by a subclass or c
1onfig, not hardcoded with ",(0,r.jsx)(n.code,{children:"new SpecificThing()"})," scattered everywhere."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Analogy:"})," A logistics company that ships by ",(0,r.jsx)(n.code,{children:"Truck"})," by default. If it expands into sea freight, you don't want to rewrite every place that calls ",(0,r.jsx)(n.code,{children:"new Truck()"})," \u2014 you want one place that decides ",(0,r.jsx)(n.em,{children:"which"})," transport to create."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"interface Notification {\n send(message: string): void;\n}\n\nclass EmailNotification implements Notification {\n send(message: string) { console.log(`Email: ${message}`); }\n}\n\nclass SmsNotification implements Notification {\n send(message: string) { console.log(`SMS: ${message}`); }\n}\n\nabstract class NotifierCreator {\n abstract createNotification(): Notification;\n\n notify(message: string) {\n const notification = this.createNotification();\n notification.send(message);\n }\n}\n\nclass EmailNotifier extends NotifierCreator {\n createNotification(): Notification { return new EmailNotification(); }\n}\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"})," When object creation logic is nontrivial (branching, config-driven) and repeated in multiple places. If you're only ever creating one type of object, you don't need this \u2014 just call the constructor."]}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h3,{id:"builder",children:"Builder"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"})," An object needs many optional parameters, and a constructor with 8 optional args (or a giant config object with unclear required-vs-optional fields) is unreadable and error-prone."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:'class HttpRequestBuilder {\n private url = "";\n private method = "GET";\n private headers: Record<string, string> = {};\n private body?: string;\n\n setUrl(url: string) { this.url = url; return this; }\n setMethod(method: string) { this.method = method; return this; }\n addHeader(key: string, value: string) { this.headers[key] = value; return this; }\n setBody(body: string) { this.body = body; return this; }\n\n build() {\n return { url: this.url, method: this.method, headers: this.headers, body: this.body };\n }\n}\n\nconst request = new HttpRequestBuilder()\n .setUrl("/api/users")\n .setMethod("POST")\n .addHeader("Content-Type", "application/json")\n .setBody(JSON.stringify({ name: "Alex" }))\n .build();\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"})," Objects with many optional fields, or when you want step-by-step construction that reads clearly (this is basically what fluent APIs like query builders, test-data factories, and request builders are). You've almost certainly used this pattern already without naming it."]}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h2,{id:"structural-patterns",children:"Structural Patterns"}),"\n",(0,r.jsx)(n.h3,{id:"adapter",children:"Adapter"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"})," You have two interfaces that don't match (your code expects ",(0,r.jsx)(n.code,{children:".getData()"}),", a third-party library gives you ",(0,r.jsx)(n.code,{children:".fetchPayload()"}),"), and you can't or don't want to modify either side."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Analogy:"})," A power plug adapter. It doesn't change the wall socket or your laptop \u2014 it sits between them and translates."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"// Third-party class you can't change\nclass LegacyPaymentGateway {\n makePayment(amountInCents: number) { /* ... */ }\n}\n\n// Interface your app expects\ninterface PaymentProcessor {\n pay(amountInDollars: number): void;\n}\n\nclass PaymentGatewayAdapter implements PaymentProcessor {\n constructor(private legacyGateway: LegacyPaymentGateway) {}\n\n pay(amountInDollars: number) {\n this.legacyGateway.makePayment(Math.round(amountInDollars * 100));\n }\n}\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"})," Integrating third-party/legacy code, wrapping an old API while migrating to a new one, or normalizing multiple providers (e.g. different payment gateways) behind one interface your app talks to."]}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h3,{id:"decorator",children:"Decorator"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"})," You want to add behavior to an individual object dynamically, without subclassing every possible combination (imagine ",(0,r.jsx)(n.code,{children:"Coffee"}),", ",(0,r.jsx)(n.code,{children:"CoffeeWithMilk"}),", ",(0,r.jsx)(n.code,{children:"CoffeeWithMilkAndSugar"}),", ",(0,r.jsx)(n.code,{children:"CoffeeWithMilkAndSugarAndCream"}),"... this explodes fast)."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:'interface Coffee {\n cost(): number;\n description(): string;\n}\n\nclass SimpleCoffee implements Coffee {\n cost() { return 2; }\n description() { return "Coffee"; }\n}\n\nabstract class CoffeeDecorator implements Coffee {\n constructor(protected wrapped: Coffee) {}\n cost(): number { return this.wrapped.cost(); }\n description(): string { return this.wrapped.description(); }\n}\n\nclass MilkDecorator extends CoffeeDecorator {\n cost() { return super.cost() + 0.5; }\n description() { return super.description() + " + Milk"; }\n}\n\nclass SugarDecorator extends CoffeeDecorator {\n cost() { return super.cost() + 0.2; }\n description() { return super.description() + " + Sugar"; }\n}\n\nconst order = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));\nconsole.log(order.description(), order.cost());
1 // "Coffee + Milk + Sugar", 2.7\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"})," Middleware chains (Express/Koa middleware is literally this pattern), stream wrappers, UI component composition (higher-order components in React are a decorator variant). Very common in the wild, rarely named out loud."]}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h3,{id:"facade",children:"Facade"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"})," A subsystem has a complicated set of classes/steps to use correctly. You want to expose one simple interface that hides that complexity for the common case."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:'class AudioEncoder { encode() { /* ... */ } }\nclass VideoEncoder { encode() { /* ... */ } }\nclass SubtitleMerger { merge() { /* ... */ } }\nclass FileCompressor { compress() { /* ... */ } }\n\n// Facade\nclass VideoConverter {\n convert(inputFile: string) {\n const audio = new AudioEncoder();\n const video = new VideoEncoder();\n const subs = new SubtitleMerger();\n const compressor = new FileCompressor();\n\n audio.encode();\n video.encode();\n subs.merge();\n compressor.compress();\n\n return "output.mp4";\n }\n}\n\n// Caller just does this:\nnew VideoConverter().convert("input.mov");\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"}),' Any time you\'d otherwise be exposing every internal class of a subsystem to callers who just want "the simple 90% use case." Most well-designed SDK entry points/',(0,r.jsx)(n.code,{children:"index.ts"})," files are informal facades."]}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h3,{id:"proxy",children:"Proxy"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"})," You want to control access to an object \u2014 lazy-load it, cache its results, log calls to it, check permissions \u2014 without the caller knowing the difference."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"interface ImageLoader {\n display(): void;\n}\n\nclass HighResImage implements ImageLoader {\n constructor(private path: string) {\n console.log(`Loading expensive image from ${path}...`);\n }\n display() { console.log(`Displaying ${this.path}`); }\n}\n\nclass LazyImageProxy implements ImageLoader {\n private realImage?: HighResImage;\n constructor(private path: string) {}\n\n display() {\n if (!this.realImage) {\n this.realImage = new HighResImage(this.path); // only loads when actually needed\n }\n this.realImage.display();\n }\n}\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"})," Lazy initialization, access control/auth checks, caching layers, logging/monitoring wrappers. If you've used an ORM's \"lazy loaded relation,\" you've used a proxy."]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsxs)(n.em,{children:["Note: Proxy and Decorator look structurally similar (both wrap an object behind the same interface). The difference is intent \u2014 Decorator ",(0,r.jsx)(n.strong,{children:"adds"})," behavior/responsibility, Proxy ",(0,r.jsx)(n.strong,{children:"controls access"})," to the same behavior."]})}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h2,{id:"behavioral-patterns",children:"Behavioral Patterns"}),"\n",(0,r.jsx)(n.h3,{id:"strategy",children:"Strategy"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"})," You have several interchangeable algorithms for doing the same job (e.g. different sorting methods, different pricing rules, different payment methods) and want to swap between them at runtime without a pile of ",(0,r.jsx)(n.code,{children:"if/else"})," or ",(0,r.jsx)(n.code,{children:"switch"})," statements."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"interface DiscountStrategy {\n apply(price: number): number;\n}\n\nclass NoDiscount implements DiscountStrategy {\n apply(price: number) { return price; }\n}\n\nclass PercentageDiscount implements DiscountStrategy {\n constructor(private percent: number) {}\n apply(price: number) { return price * (1 - this.percent / 100); }\n}\n\nclass Cart {\n constructor(private strategy: DiscountStrategy) {}
1\n checkout(price: number) { return this.strategy.apply(price); }\n}\n\nnew Cart(new PercentageDiscount(10)).checkout(100); // 90\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"})," This is probably the single most useful everyday pattern. Any time you see a big ",(0,r.jsx)(n.code,{children:"switch"})," statement picking between behaviors based on a type/flag, that's a strategy pattern trying to happen."]}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h3,{id:"observer",children:"Observer"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"})," One object's state changes, and a variable number of other objects need to know about it, without the source object needing to know who they are."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:'type Listener<T> = (data: T) => void;\n\nclass EventEmitter<T> {\n private listeners: Listener<T>[] = [];\n\n subscribe(listener: Listener<T>) {\n this.listeners.push(listener);\n return () => { this.listeners = this.listeners.filter(l => l !== listener); };\n }\n\n emit(data: T) {\n this.listeners.forEach(listener => listener(data));\n }\n}\n\nconst onUserCreated = new EventEmitter<{ id: string }>();\nonUserCreated.subscribe(user => console.log(`Send welcome email to ${user.id}`));\nonUserCreated.subscribe(user => console.log(`Log signup for ${user.id}`));\n\nonUserCreated.emit({ id: "u_123" });\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"})," This is the backbone of event-driven systems \u2014 DOM events, Node's ",(0,r.jsx)(n.code,{children:"EventEmitter"}),", pub/sub systems, React state subscriptions, RxJS. If you've ever called ",(0,r.jsx)(n.code,{children:".on()"}),", ",(0,r.jsx)(n.code,{children:".addEventListener()"}),", or ",(0,r.jsx)(n.code,{children:".subscribe()"}),", you've used this pattern."]}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h3,{id:"command",children:"Command"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Problem it solves:"}),' You want to turn "a request to do something" into a standalone object \u2014 so it can be queued, logged, undone, or passed around like data instead of being an immediate function call.']}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"interface Command {\n execute(): void;\n undo(): void;\n}\n\nclass AddTextCommand implements Command {\n constructor(private document: string[], private text: string) {}\n execute() { this.document.push(this.text); }\n undo() { this.document.pop(); }\n}\n\nclass CommandHistory {\n private history: Command[] = [];\n\n run(command: Command) {\n command.execute();\n this.history.push(command);\n }\n\n undoLast() {\n this.history.pop()?.undo();\n }\n}\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"When to actually use it:"}),' Undo/redo stacks, task queues/job systems, macro recording, transactional operations. Redux actions are basically Command objects \u2014 a plain description of "what should happen" separated from "what actually does it."']}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h2,{id:"the-rest-know-the-name-look-up-the-details-when-you-need-them",children:"The rest (know the name, look up the details when you need them)"}),"\n",(0,r.jsxs)(n.p,{children:["These come up less often day-to-day. Keeping the one-liners here so I recognize the shape of the problem when it shows up, and can go read the ",(0,r.jsx)(n.a,{href:"https://refactoring.guru/design-patterns",children:"refactoring.guru"})," page for the one I need at that moment."]}),"\n",(0,r.jsx)(n.h3,{id:"creational",children:"Creational"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Abstract Factory"})," \u2014 a factory of factories; produces families of related objects (e.g. a whole UI kit: ",(0,r.jsx)(n.code,{children:"WindowsButton"})," + ",(0,r.jsx)(n.code,{children:"WindowsCheckbox"})," vs ",(0,r.jsx)(n.code,{children:"MacButton"})," + ",(0,r.jsx)(n.code,{children:"MacCheckbox"}),") without specifying their concrete classes."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Prototype"})," \u2014 create new objects by cloning an existing instance instead of building from scratch, useful when construction is expensive or the object's exact class is unknown ahead of time."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"structural",children:"Structural"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Composite"})," \u2014 treat individual objects and groups of objects uniformly through a shared interface; classic for tree structures like file systems or UI component trees."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Bridge"})," \u2014 decouple an abstraction from its implementation so the two can vary independently (e.g. separating a ",(0,r.jsx)(n.code,{children:"Shape"})," hierarchy from a ",(0,r.jsx)(n.code,{children:"Renderer"})," hierarchy so you don't get a class explosion of ",(0,r.jsx)(n.code,{children:"VectorCircle"}
1),", ",(0,r.jsx)(n.code,{children:"RasterCircle"}),", ",(0,r.jsx)(n.code,{children:"VectorSquare"}),"...)."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Flyweight"})," \u2014 share common, immutable parts of many similar objects to save memory (e.g. sharing character glyph data across millions of rendered text characters)."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"behavioral",children:"Behavioral"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Chain of Responsibility"})," \u2014 pass a request along a chain of handlers until one handles it (e.g. middleware pipelines, event bubbling, support-ticket escalation)."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Iterator"})," \u2014 provide a standard way to traverse a collection without exposing its internal structure. If you've used a ",(0,r.jsx)(n.code,{children:"for...of"})," loop or implemented ",(0,r.jsx)(n.code,{children:"[Symbol.iterator]"}),", you've used this."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Mediator"})," \u2014 centralize how a set of objects communicate, so they talk to a mediator instead of directly to each other (e.g. an air traffic control tower; chat room server)."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Memento"})," \u2014 capture and restore an object's internal state without violating encapsulation, typically for undo functionality (pairs well with Command)."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"State"})," \u2014 let an object change its behavior when its internal state changes, by delegating to state-specific objects instead of a giant conditional (e.g. a ",(0,r.jsx)(n.code,{children:"TrafficLight"})," with ",(0,r.jsx)(n.code,{children:"RedState"}),"/",(0,r.jsx)(n.code,{children:"GreenState"}),"/",(0,r.jsx)(n.code,{children:"YellowState"})," objects)."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Template Method"})," \u2014 define the skeleton of an algorithm in a base class, letting subclasses override specific steps without changing the overall structure (common in test framework ",(0,r.jsx)(n.code,{children:"setUp"}),"/",(0,r.jsx)(n.code,{children:"test"}),"/",(0,r.jsx)(n.code,{children:"tearDown"})," hooks)."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Visitor"})," \u2014 separate an algorithm from the object structure it operates on, so you can add new operations without modifying the objects themselves (common in compilers/ASTs)."]}),"\n"]}),"\n",(0,r.jsx)(n.hr,{}),"\n",(0,r.jsx)(n.h2,{id:"practical-takeaways",children:"Practical takeaways"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Don't reach for a pattern first."})," Write the straightforward version. If you notice the same awkward shape (giant switch statement, exploding subclass hierarchy, tangled object creation) showing up more than once, ",(0,r.jsx)(n.em,{children:"then"})," reach for the matching pattern."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Strategy, Observer, Decorator, Factory Method, and Adapter"})," are the ones I'll actually use regularly. The rest are worth recognizing by name so I don't reinvent them badly, but I won't force them in."]}),"\n",(0,r.jsxs)(n.li,{children:["Frameworks and libraries already implement most of these for you (middleware = Decorator/Chain of Responsibility, event emitters = Observer, DI containers = Factory + Singleton done properly). Recognizing the pattern under the hood makes the framework's API make ",(0,r.jsx)(n.em,{children:"sense"})," instead of feeling arbitrary."]}),"\n",(0,r.jsxs)(n.li,{children:["Revisit ",(0,r.jsx)(n.a,{href:"https://refactoring.guru/design-patterns",children:"refactoring.guru/design-patterns"})," whenever a pattern from the reference list above actually shows up in a real problem \u2014 that's the moment it'll actually stick."]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.