design_patterns_structural 32 Q&As

Design Patterns Structural FAQ & Answers

32 expert Design Patterns Structural answers researched from official documentation. Every answer cites authoritative sources you can verify.

unknown

32 questions
A

Adapter converts interface of class into another interface clients expect. Enables incompatible interfaces to work together. Use when: (1) Want to use existing class with incompatible interface, (2) Integrate third-party library with different interface, (3) Create reusable class that works with unrelated classes. Pattern: interface Target {request()}; class Adapter implements Target {constructor(private adaptee: Adaptee) {} request() {return this.adaptee.specificRequest();}}. Example: Payment gateway adapter - interface PaymentProcessor {process(amount)}, class StripeAdapter implements PaymentProcessor {constructor(private stripe: Stripe) {} process(amount) {return this.stripe.charges.create({amount});}}. Benefits: Decouples client from implementation, single responsibility (adapt interface), reuse existing code. Use for: Legacy code integration, third-party APIs, testing (adapt real service to mock interface).

99% confidence
A

Class Adapter uses multiple inheritance (extends adaptee, implements target), Object Adapter uses composition (wraps adaptee instance). Class Adapter: class Adapter extends Adaptee implements Target. Direct access to adaptee methods, can override behavior. Only works in languages supporting multiple inheritance (C++, Python). Object Adapter: class Adapter implements Target {constructor(private adaptee: Adaptee)}. Works in all languages (TypeScript, Java, C#), more flexible (runtime adapter selection), follows composition over inheritance. Example Class: class StripeAdapter extends StripeSDK implements PaymentProcessor. Example Object: class StripeAdapter implements PaymentProcessor {constructor(private stripe: StripeSDK)}. Recommendation: Use Object Adapter (99% of cases). More flexible, testable (inject mock adaptee), follows SOLID principles. Only use Class Adapter if need to override protected methods or language supports/requires it.

99% confidence
A

Adapter for interface conversion, Facade for simplifying complex API. Adapter pattern: Create interface matching your domain, adapt third-party SDK. Example: interface PaymentService {processPayment(amount: number): Promise}; class StripeAdapter implements PaymentService {constructor(private stripe: Stripe) {} async processPayment(amount) {const result = await this.stripe.charges.create({amount: amount * 100, currency: 'usd'}); return {success: result.status === 'succeeded', transactionId: result.id};}}. Benefits: Swap providers (Stripe → PayPal) without changing business logic, test with mock adapter. Facade pattern: Wrap multiple API calls in single method. Example: class CheckoutFacade {constructor(private payment: PaymentService, private inventory: InventoryAPI, private email: EmailAPI) {} async checkout(order: Order) {await this.inventory.reserve(order.items); const payment = await this.payment.processPayment(order.total); await this.email.sendConfirmation(order); return payment;}}. Use Adapter for backward compatibility when API changes: class LegacyAPIAdapter implements NewAPI {constructor(private legacy: LegacyAPI) {} newMethod() {return this.legacy.oldMethod().then(transform);}}. TypeScript 2024: Use generics for type-safe adapters: class APIAdapter<T, U> {transform(input: T): U}.

99% confidence
A

Facade provides simplified interface to complex subsystem. Hides complexity behind simple API. Use when: (1) Complex subsystem with many classes/dependencies, (2) Want to decouple clients from subsystem, (3) Layer architecture (facade per layer). Pattern: class OrderFacade {constructor(private inventory: Inventory, private payment: Payment, private shipping: Shipping) {} placeOrder(order) {this.inventory.reserve(order.items); this.payment.charge(order.total); this.shipping.schedule(order);}}. Client uses simple placeOrder() instead of coordinating three subsystems. Benefits: Simplifies client code, decouples client from subsystem (can change internals), provides entry point. Example: Database facade wrapping connection pool + query builder + transaction manager. Don't confuse with Adapter: Facade simplifies (one to many), Adapter converts interfaces (one to one). Use Facade to hide complexity, Adapter to enable compatibility.

99% confidence
A

Decorator attaches additional responsibilities to object dynamically. Alternative to subclassing for extending functionality. Use when: (1) Add features at runtime (not compile-time), (2) Avoid class explosion from combinations (text bold + italic + underline = 8 subclasses), (3) Responsibilities can be added/removed. Pattern: interface Component {operation()}; class Decorator implements Component {constructor(protected component: Component) {} operation() {return this.component.operation();}}. Concrete: class BoldDecorator extends Decorator {operation() {return '' + this.component.operation() + '';}}. Usage: let text = new PlainText('hello'); text = new BoldDecorator(text); text = new ItalicDecorator(text). Benefits: Flexible (combine any decorators), follows Open/Closed Principle, single responsibility (one feature per decorator). Example: Express middleware (decorates request/response), Java streams (BufferedReader decorates FileReader).

99% confidence
A

Use interface for component and decorator, concrete decorators extend base decorator. Pattern: interface Coffee {cost(): number; description(): string}. class SimpleCoffee implements Coffee {cost() {return 5;} description() {return 'Simple coffee';}}. abstract class CoffeeDecorator implements Coffee {constructor(protected coffee: Coffee) {}} abstract cost(): number; abstract description(): string;}. class MilkDecorator extends CoffeeDecorator {cost() {return this.coffee.cost() + 1;} description() {return this.coffee.description() + ', milk';}}. Usage: let coffee: Coffee = new SimpleCoffee(); coffee = new MilkDecorator(coffee); coffee = new SugarDecorator(coffee); console.log(coffee.description(), coffee.cost()). TypeScript advantage: Type-safe chaining. Can use generics: class LogDecorator {constructor(private component: T)}. Real example: HTTP request interceptors, logging decorators, caching decorators.

99% confidence
A

HOCs wrap components to add behavior, implementing Decorator pattern. Still relevant in 2024 alongside hooks for cross-cutting concerns. Pattern: function withAuth

(Component: React.ComponentType

) {return (props: P) => {if (!isAuthenticated()) return ; return <Component {...props} />;}}. Usage: const ProtectedPage = withAuth(Dashboard). HOC wraps component, adds authentication check. Multiple decorators: const Enhanced = withAuth(withLogging(withAnalytics(Dashboard))). Benefits: Reusable logic (apply to any component), composition (stack multiple HOCs), props injection. 2025 best practices: Use HOCs for: (1) Authorization/authentication, (2) Layout wrappers, (3) Data fetching with Suspense, (4) Error boundaries. Use hooks for: Component-level state, side effects. Combine: HOC provides context, hook consumes it. Example: withTheme HOC + useTheme hook. TypeScript: Preserve prop types with generics. Modern alternative: Render props, but HOCs cleaner for middleware-style logic. Don't overuse: More than 3 HOCs signals need for composition pattern or custom hook.

99% confidence
A

Middleware executes BEFORE route handlers, processing every request globally or per route. Implements function signature: (req, res, next) => void. Apply globally: app.use(LoggerMiddleware) or per module: @Module({providers: [LoggerMiddleware]}). Custom middleware: @Injectable() export class LoggerMiddleware implements NestMiddleware {use(req: Request, res: Response, next: NextFunction) {console.log(${req.method} ${req.url}); next();}}. Configure: export class AppModule {configure(consumer: MiddlewareConsumer) {consumer.apply(LoggerMiddleware).forRoutes('*');}}. Use for: Authentication token validation (extract user), CORS handling, request logging, body parsing, rate limiting, compression. Executes first in NestJS pipeline (before guards/interceptors). Multiple middleware execute in binding order. NestJS 10+ supports async middleware with await. Use middleware when: Need to process ALL requests, modify req/res objects globally, implement cross-cutting concerns before routing.

99% confidence
A

Guards execute AFTER middleware, BEFORE route handlers. Determine if request proceeds to controller. Implement CanActivate interface with canActivate() method returning boolean or Promise. Pattern: @Injectable() export class RolesGuard implements CanActivate {canActivate(context: ExecutionContext): boolean {const request = context.switchToHttp().getRequest(); const user = request.user; return user?.roles.includes('admin');}}. Apply with decorator: @UseGuards(RolesGuard) on controller or route. Access metadata: this.reflector.get<string[]>('roles', context.getHandler()). Set metadata: @SetMetadata('roles', ['admin']). Execution order: Global guards → Controller guards → Route guards. Use for: Role-based access control (RBAC), permission checks, feature flags, subscription validation, IP whitelisting. Guards have access to ExecutionContext (richer than middleware). Return false or throw UnauthorizedException to block request. NestJS 10+: Guards run after middleware sets req.user. Combine with decorators: @Roles('admin') @UseGuards(RolesGuard).

99% confidence
A

Interceptors execute BEFORE and AFTER route handler using RxJS operators. Implement NestInterceptor interface with intercept(context: ExecutionContext, next: CallHandler) method. Pattern: @Injectable() export class LoggingInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler): Observable {const now = Date.now(); return next.handle().pipe(tap(() => console.log(Executed in ${Date.now() - now}ms)));}}. Apply: @UseInterceptors(LoggingInterceptor). Execution: Interceptors wrap handler in first-in-last-out order (onion model). Use for: Response transformation (map data), caching responses, logging execution time, error handling, timeout handling (timeout operator), serialization (ClassSerializerInterceptor). Access response: pipe(map(data => ({data, timestamp: Date.now()}))). Global interceptor: app.useGlobalInterceptors(new LoggingInterceptor()). NestJS 10+ built-in: CacheInterceptor (cache responses), ClassSerializerInterceptor (transform DTOs). Combine: Stack multiple interceptors, outer wraps inner.

99% confidence
A

NestJS request lifecycle: (1) Middleware (global → module-bound), (2) Guards (global → controller → route), (3) Interceptors before (global → controller → route), (4) Pipes (parameter validation/transformation), (5) Route handler (controller method), (6) Interceptors after (route → controller → global, reversed), (7) Exception filters (route → controller → global). Key: Guards/Interceptors execute in binding order going in, reversed order going out. Interceptors wrap using RxJS observables (first-in-last-out). Exception filters use lowest-level-first (route → global). Example flow: app.use(logger) → @UseGuards(AuthGuard) → @UseInterceptors(LogInterceptor) → @UsePipes(ValidationPipe) → getUsers() → LogInterceptor.after → CatchFilter. Decorators (@SetMetadata) are design-time only, read by guards/interceptors at runtime via Reflector. Use this order to plan where logic belongs: authentication in middleware, authorization in guards, transformation in interceptors.

99% confidence
A

Custom decorators extract data from request context or attach metadata. Three types: (1) Parameter decorators, (2) Method decorators, (3) Class decorators. Parameter decorator: export const User = createParamDecorator((data: string, ctx: ExecutionContext) => {const request = ctx.switchToHttp().getRequest(); return data ? request.user?.[data] : request.user;}). Usage: getProfile(@User() user: UserEntity) or @User('id') userId: string. Method/Class decorator (metadata): export const Roles = (...roles: string[]) => SetMetadata('roles', roles). Usage: @Roles('admin', 'moderator') getUsers(). Read metadata in guard: const roles = this.reflector.get<string[]>('roles', context.getHandler()). Combine decorators: export const Auth = (...roles: string[]) => applyDecorators(Roles(...roles), UseGuards(AuthGuard, RolesGuard)). Use for: Extract user from JWT, role-based access, feature flags, API versioning, rate limiting metadata. NestJS 10+: Full TypeScript 5.0 decorator support (Stage 3 TC39). Best practice: Single responsibility per decorator, avoid business logic inside decorators.

99% confidence
A

Decision criteria: Use middleware for: (1) Global request processing (ALL routes), (2) Early validation before routing (authentication token extraction), (3) Request/response object modification, (4) Third-party integration (helmet, cors, compression). Use guards for: (1) Authorization checks (role-based, permission-based), (2) Route-specific access control, (3) Feature flags, (4) Subscription validation. Guards should return boolean (allow/deny). Use interceptors for: (1) Response transformation (data mapping), (2) Caching responses, (3) Logging execution time, (4) Timeout handling, (5) Error handling/retry logic. Interceptors wrap handler with RxJS. Rule of thumb: Middleware → Guards → Interceptors follows authentication → authorization → transformation pattern. Example: Middleware extracts JWT, Guard checks user.role, Interceptor caches response. Don't use middleware for authorization (use guards), don't use guards for transformation (use interceptors), don't use interceptors for authentication (use middleware). Stack multiple: app.use(auth) → @UseGuards(RolesGuard) → @UseInterceptors(CacheInterceptor).

99% confidence
A

Proxy provides surrogate controlling access to another object. Acts as intermediary. Four types: (1) Protection Proxy (access control), (2) Virtual Proxy (lazy initialization), (3) Remote Proxy (represents remote object), (4) Caching Proxy (cache results). Pattern: interface Subject {request()}; class RealSubject implements Subject {request() {/* expensive operation */}}. class ProxySubject implements Subject {constructor(private real: RealSubject) {} request() {if (this.checkAccess()) return this.real.request();}}. Use when: Need lazy loading (create expensive object when needed), access control (check permissions), logging (track requests), caching (avoid repeated calls). Example: Image proxy loads image only when displayed, API proxy adds authentication headers, database proxy pools connections. Benefits: Controls access without modifying real object, adds behavior transparently. JavaScript ES6 Proxy: built-in mechanism for intercepting operations on objects.

99% confidence
A

Proxy controls access (restricts), Decorator adds responsibilities (enhances). Intent: Proxy manages access to object (black box - client unaware), Decorator augments object behavior (white box - client aware). Lifecycle: Proxy creates/manages real object (compile-time binding, instantiates subject), Decorator wraps existing instance (runtime binding, requires instance). Client interaction: Proxy restricts/disempowers client (access control, lazy loading, remote proxy), Decorator enhances client capabilities (adds features visibly). Stacking: Decorators stack recursively unlimited times (text = new BoldDecorator(new ItalicDecorator(text))), Proxies singular (one proxy enough). Example Proxy: class ImageProxy {private real: Image | null = null; display() {if (!this.real) this.real = new RealImage(); this.real.display();}} - controls when image loads. Example Decorator: class ScrollDecorator extends Decorator {display() {addScrollBar(); this.component.display();}} - adds scrolling to any component. Usage: Proxy for infrastructure (connection pools, caching, security), Decorator for application logic (features, middleware, UI enhancements). Can combine but clarify intent: focus on control vs enhancement, not strict definitions.

99% confidence
A

Composite composes objects into tree structures representing part-whole hierarchies. Lets clients treat individual objects and compositions uniformly. Use when: (1) Represent hierarchies (file system, UI trees, organization charts), (2) Want clients to ignore difference between single/composite objects. Pattern: interface Component {operation()}; class Leaf implements Component {operation() {/* do work */}}. class Composite implements Component {private children: Component[] = []; add(c: Component) {this.children.push(c);} operation() {this.children.forEach(c => c.operation());}}. Example: File system - File (leaf) and Folder (composite) both implement FileSystemItem. Folder contains files/folders. Client: item.getSize() works on both. Benefits: Uniform treatment (recursive algorithms simple), easy to add new components, follows Open/Closed. Use for: DOM trees, UI components (Panel contains Buttons), menu systems, organization hierarchies.

99% confidence
A

Implement operation() in composite to recursively call children's operations. Pattern for traversal: class Composite implements Component {private children: Component[] = []; operation() {const results = []; results.push(this.doSomething()); this.children.forEach(child => results.push(child.operation())); return results.flat();}}. Depth-first by default (process node, then children). Breadth-first: use queue: const queue = [root]; while (queue.length) {const node = queue.shift(); node.doSomething(); queue.push(...node.children);}. Visitor pattern for complex traversal: interface Visitor {visitLeaf(Leaf); visitComposite(Composite)}. Component: accept(visitor: Visitor). Separates traversal logic from tree structure. Example: Calculate folder size: class SizeCalculator implements Visitor {visitFile(f) {return f.size;} visitFolder(f) {return f.children.reduce((sum, c) => sum + c.accept(this), 0);}}. Use Visitor when: Multiple operations on tree, operations change frequently.

99% confidence
A

Composite pattern in React: Parent and child components share same interface (props/methods), enabling recursive rendering. Pattern: interface TreeNode {id: string; label: string; children?: TreeNode[]}. Component: function TreeItem({node}: {node: TreeNode}) {return

  • {node.label} {node.children &&
      {node.children.map(child => )}
    }
  • ;}. Treats leaf (no children) and composite (with children) uniformly. Compound components (2024): Parent provides context, children access it. Example: <Menu.Item /><Menu.Submenu><Menu.Item /></Menu.Submenu>. Implementation: const MenuContext = createContext(); function Menu({children}) {return <MenuContext.Provider value={state}>{children}</MenuContext.Provider>;}. Menu.Item reads context. Benefits: Flexible composition, recursive structures (comments, file trees, org charts), uniform API. Real use cases: (1) File explorer: Folder and File components, both have onClick/icon/name. (2) Navigation menu: Link and Dropdown, both have render/isActive. (3) Comment thread: Comment and CommentList, nested recursively. 2025 best practice: Combine with React Server Components for tree hydration optimization. TypeScript: Use discriminated unions for leaf vs composite: type Node = {type: 'leaf'} | {type: 'composite', children: Node[]}.

    99% confidence
    A

    Bridge separates abstraction from implementation enabling independent variation. Decouples interface hierarchy from implementation hierarchy. Use when: (1) Avoid class explosion from orthogonal variations (Shape × Color = RedCircle, BlueCircle, RedRectangle, BlueRectangle = 4 classes instead of Shape + Color = 2 hierarchies), (2) Runtime implementation selection (choose platform at runtime, not compile-time), (3) Both abstraction and implementation need independent extension, (4) Platform independence (Windows/Mac/Linux). Pattern: abstract class Abstraction {constructor(protected impl: Implementation) {} operation() {this.impl.operationImpl();}}. interface Implementation {operationImpl(): void}. Concrete: class RefinedAbstraction extends Abstraction. class ConcreteImplementation implements Implementation. Example: abstract class RemoteControl {constructor(protected device: Device) {} powerOn() {this.device.turnOn();}}. interface Device {turnOn(): void; setVolume(v: number): void}. class TVRemote extends RemoteControl. class TV implements Device, class Radio implements Device. Usage: const remote = new TVRemote(new TV()) or new TVRemote(new Radio()). Benefits: Composition over inheritance (favors flexibility), reduced subclasses (n abstractions + m implementations instead of n×m classes), runtime binding. Differs from Adapter: Bridge designed upfront for parallel hierarchies, Adapter retrofits incompatible interfaces. Use Bridge for: Database drivers (JDBC), payment processors, rendering engines, cross-platform UI.

    99% confidence
    A

    Bridge pattern separates platform-agnostic logic (abstraction) from platform-specific implementations. Pattern: Define abstraction with protected implementation reference. Example: abstract class Notification {constructor(protected sender: MessageSender) {} abstract send(message: string): void;}. class EmailNotification extends Notification {send(message) {this.sender.sendMessage('email', message);}}. class SMSNotification extends Notification {send(message) {this.sender.sendMessage('sms', message);}}. Implementation interface: interface MessageSender {sendMessage(platform: string, message: string): void;}. class SendGridSender implements MessageSender {sendMessage(platform, message) {/* SendGrid API /}}. class TwilioSender implements MessageSender {sendMessage(platform, message) {/ Twilio API */}}. Usage: const notification = new EmailNotification(new SendGridSender()); notification.send('Hello') - can swap SendGrid for Mailgun without changing EmailNotification. Benefits: Add platforms (Push, Slack) without modifying abstraction, swap implementations at runtime, test with mock sender. Use cases: (1) Payment processors: PaymentMethod abstraction, Stripe/PayPal/Crypto implementations. (2) Storage: FileStorage abstraction, S3/Azure/Local implementations. (3) Logging: Logger abstraction, Console/File/Cloud implementations. 2024: Combine with DI containers for runtime configuration: @Injectable() class EmailService {constructor(@Inject(MESSAGE_SENDER) sender: MessageSender)}.

    99% confidence
    A

    Flyweight minimizes memory by sharing common data among many objects. Separates intrinsic (shared) from extrinsic (unique) state. Use when: (1) Many similar objects consuming memory, (2) Objects have sharable state, (3) Identity not important (value objects). Pattern: class Flyweight {constructor(private intrinsic: string) {} operation(extrinsic: string) {/* use both */}}. class FlyweightFactory {private flyweights = new Map(); get(key: string) {if (!this.flyweights.has(key)) this.flyweights.set(key, new Flyweight(key)); return this.flyweights.get(key);}}. Example: Text editor - Character flyweight stores font/style (intrinsic), position passed when rendering (extrinsic). 1 million characters = 26 flyweights (letters) instead of 1M objects. Benefits: Massive memory savings, CPU cache efficiency. Trade-off: Complexity (splitting state), slower object access. Use for: Game particles (sprites share texture), text rendering, map tiles.

    99% confidence
    A

    Flyweight pattern reduces memory when rendering thousands of similar objects. Pattern: Separate intrinsic (shared) from extrinsic (unique) state. Game particle system: class ParticleType {constructor(public sprite: Texture, public color: Color) {/* intrinsic - shared /}}. class Particle {constructor(private type: ParticleType, public x: number, public y: number, public velocity: Vector) {/ extrinsic - unique per particle */} render(canvas) {canvas.drawSprite(this.type.sprite, this.x, this.y, this.type.color);}}. class ParticleFactory {private types = new Map<string, ParticleType>(); getType(sprite, color) {const key = ${sprite}-${color}; if (!this.types.has(key)) this.types.set(key, new ParticleType(sprite, color)); return this.types.get(key);}}. Usage: 10,000 particles with 5 sprite types = 5 ParticleType objects (shared) + 10,000 Particle objects (only position/velocity). Memory: 5 textures vs 10,000 textures. Data visualization: Share chart style (colors, fonts) across 100,000 data points. 2024 benchmarks: 80-95% memory reduction, 2-3x faster rendering (cache locality). Use with: Object pooling (reuse Particle instances), spatial partitioning (only render visible), instanced rendering (GPU shares geometry). JavaScript: WeakMap for automatic garbage collection of unused flyweights.

    99% confidence
    A

    Create proxy that instantiates real object on first access. Pattern: interface ExpensiveService {operation()}. class RealService implements ExpensiveService {constructor() {/* expensive initialization /} operation() {/ do work */}}. class LazyProxy implements ExpensiveService {private real: RealService | null = null; private getRealService() {if (!this.real) this.real = new RealService(); return this.real;} operation() {return this.getRealService().operation();}}. Usage: const service = new LazyProxy(); service.operation() - creates RealService on first call. Benefits: Faster startup (defer creation), memory efficient (only if used), transparent to client. ES6 Proxy: const lazy = new Proxy({}, {get(target, prop) {if (!target.real) target.real = new RealService(); return target.real[prop];}}). Use for: Heavy resources (DB, cache, ML models), optional dependencies, plugin systems.

    99% confidence
    A

    Facade simplifies complex subsystem (one to many), Adapter converts incompatible interface (one to one). Facade: Client wants simple interface to complex system. Facade provides high-level operations hiding subsystem complexity. Example: OrderFacade.placeOrder() coordinates Inventory, Payment, Shipping - client doesn't know about subsystems. Adapter: Client expects specific interface, existing class has different interface. Adapter converts. Example: StripeAdapter implements PaymentProcessor interface, adapts Stripe SDK. Key difference: Facade is new simplified interface, Adapter makes existing interface compatible with expected interface. Intent: Facade for simplification, Adapter for compatibility. Can combine: Facade using adapters internally to unify subsystem interfaces. Use Facade when: Hiding complexity. Use Adapter when: Interface mismatch. Both promote loose coupling but different purposes.

    99% confidence
    A

    ES6 Proxy intercepts object operations (get, set, has, delete). Use for: (1) Lazy Loading: new Proxy(target, {get(t, prop) {if (!t.loaded) load(t); return t[prop];}}). (2) Validation: set(t, prop, value) {if (typeof value !== 'string') throw Error; t[prop] = value;}. (3) Logging: get(t, prop) {console.log(Accessing ${prop}); return t[prop];}. (4) Access Control: get(t, prop) {if (prop.startsWith('_')) throw Error('Private'); return t[prop];}. (5) Default Values: get(t, prop) {return prop in t ? t[prop] : 'default';}. Example Protection Proxy: const protected = new Proxy(obj, {get(t, p) {if (user.hasAccess(p)) return t[p]; throw Error('Access denied');}}). Benefits: No wrapper class needed, transparent, dynamic behavior. Trade-offs: Performance overhead, debugging harder (stack traces show Proxy). Use for: Development tools, testing mocks, metaprogramming. Production: Consider explicit patterns (maintainability).

    99% confidence
    A

    ES6 Proxy enables advanced patterns: validation, reactive programming, ORM, mock generation. Reactive proxy (observe changes): function reactive(obj: T, onChange: () => void): T {return new Proxy(obj, {set(target, prop, value) {target[prop] = value; onChange(); return true;}});}. Usage: const state = reactive({count: 0}, () => render()); state.count++ - triggers render. Type-safe validation: function validated(obj: T, rules: Record<keyof T, (val: any) => boolean>): T {return new Proxy(obj, {set(target, prop, value) {if (!rules[prop]?.(value)) throw Error(Invalid ${String(prop)}); target[prop] = value; return true;}});}. ORM pattern (lazy load relations): const user = new Proxy({id: 1}, {get(target, prop) {if (prop === 'posts' && !target.posts) target.posts = fetchPosts(target.id); return target[prop];}}). Mock generation (auto-stub): function autoMock(): T {return new Proxy({} as T, {get: () => vi.fn()}) as T;}. 2024 use cases: (1) Vue 3 reactivity system (Proxy-based), (2) TypeORM lazy relations, (3) GraphQL resolvers (dynamic field access), (4) Immer immutability (write to proxy, get immutable copy). Performance: 2-10x slower than direct access, avoid in hot paths. TypeScript: Use generics to preserve types: function createProxy(target: T): T.

    99% confidence
    A

    Prefer composition in 99% of cases. Composition: Has-a relationship, flexible, runtime behavior changes. Patterns using composition: Decorator, Bridge, Composite, Proxy, Adapter (object). Inheritance: Is-a relationship, static, tight coupling. Example inheritance problem: Class Adapter extends Adaptee - can't adapt multiple classes, can't swap adaptee. Example composition solution: Adapter {constructor(adaptee: Adaptee)} - flexible, testable, multiple implementations. Benefits of composition: (1) Runtime behavior (inject different implementations), (2) Test isolation (mock dependencies), (3) Avoid fragile base class, (4) Multiple behaviors (decorate multiple times), (5) Follows SOLID (DIP, ISP, OCP). When to use inheritance: True is-a relationships, template method pattern, framework extension points requiring overrides. Rule: Start with composition, use inheritance only when polymorphism needed and relationship truly is-a. Modern approach: Interfaces + composition over class hierarchies.

    99% confidence
    A

    Test structural patterns by mocking wrapped objects and verifying composition. Adapter testing: Mock adaptee, verify adapter calls correct methods with correct transformations. Example: const mockStripe = {charges: {create: vi.fn()}}; const adapter = new StripeAdapter(mockStripe); await adapter.processPayment(100); expect(mockStripe.charges.create).toHaveBeenCalledWith({amount: 10000, currency: 'usd'}). Verify transformation (dollars to cents). Decorator testing: Test decorators independently, then test composition. Example: const base = new SimpleCoffee(); expect(base.cost()).toBe(5); const decorated = new MilkDecorator(base); expect(decorated.cost()).toBe(6); const double = new MilkDecorator(decorated); expect(double.cost()).toBe(7). Verify chaining works. Proxy testing: Verify proxy controls access/caching. Example: const mock = vi.fn(); const proxy = new CachingProxy(mock); proxy.get('key'); proxy.get('key'); expect(mock).toHaveBeenCalledOnce() - verify cache works. Composite testing: Test leaf and composite separately, then test tree operations. Facade testing: Mock all subsystems, verify orchestration. Integration tests: Test with real implementations, verify end-to-end behavior. Use dependency injection for all patterns - makes mocking trivial. 2024 tools: Vitest for mocking, Testing Library for React patterns.

    99% confidence
    A

    TypeScript decorators (Stage 3 TC39, stable in TS 5.0+) enable declarative pattern application. Enable: tsconfig.json: {"experimentalDecorators": true, "emitDecoratorMetadata": true}. Method decorator (logging): function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {const original = descriptor.value; descriptor.value = function(...args) {console.log(Calling ${propertyKey} with, args); return original.apply(this, args);};}. Usage: class Service {@Log getData() {return 'data';}}. Class decorator (singleton): function Singleton<T extends {new(...args: any[]): {}}>(constructor: T) {let instance: T; return class extends constructor {constructor(...args) {if (instance) return instance; super(...args); instance = this as T;}}}. Usage: @Singleton class Database {}. Parameter decorator (dependency injection): NestJS: constructor(@Inject('CONFIG') config: Config). Property decorator (validation): class CreateUserDTO {@IsEmail() email: string; @MinLength(8) password: string}. Combine decorators: @UseGuards(AuthGuard) @UseInterceptors(LoggingInterceptor) @Get(). 2024 status: Decorators standard in NestJS, Angular, TypeORM, NestJS. Avoid for: Simple logic (use functions), performance-critical paths (reflection overhead). Metadata reflection: reflect-metadata library enables runtime type inspection for DI containers.

    99% confidence
    A

    Axios interceptors act as proxies wrapping HTTP requests/responses. Request interceptor proxies outgoing requests: axios.interceptors.request.use(config => {config.headers.Authorization = Bearer ${token}; return config;}). Response interceptor proxies incoming responses: axios.interceptors.response.use(response => {return transformData(response.data);}). Proxy control: Interceptors can modify, reject, or replace requests/responses transparently. Example: Logging interceptor logs all requests without modifying client code. Security interceptor validates permissions before sending request. Caching interceptor returns cached response without making actual request. Implementation: Each interceptor is a handler in chain - can be considered a proxy to the actual HTTP call. Benefits: Centralized cross-cutting concerns, transparent request/response modification, consistent error handling. Real use cases: Authentication, logging, caching, retry logic, request validation. The interceptor chain follows Proxy pattern - client unaware of intermediary processing.

    99% confidence
    A

    Implement caching with request interceptor checking cache, response interceptor storing results. Pattern: const cache = new Map(); // Request interceptor checks cache first axios.interceptors.request.use(config => {const key = config.url + JSON.stringify(config.params); const cached = cache.get(key); if (cached && Date.now() < cached.expires) {config.adapter = () => Promise.resolve({data: cached.data, status: 200, headers: {}, config});} return config;}); // Response interceptor stores results axios.interceptors.response.use(response => {const key = response.config.url + JSON.stringify(response.config.params); cache.set(key, {data: response.data, expires: Date.now() + 60000}); return response;}). TTL support: Automatic expiration, stale-while-revalidate, cache invalidation. Advanced: Use axios-cache-interceptor library with built-in storage adapters (memory, localStorage, Redis). Benefits: 21x performance improvement for repeated requests, reduced API costs, offline resilience. Use cases: Static data (country lists), dashboard metrics (5-min cache), user profiles (session cache). Configure per-request: axios.get('/data', {adapter: 'cache', ttl: 300000}).

    99% confidence
    A

    Axios built-in interceptors enable structural patterns with minimal code. Benefits: (1) Middleware pattern - chainable interceptors for composable logic, (2) Global error handling - define once, reuse everywhere (avoid repetition in every request), (3) Separation of concerns - divide logic into modules (auth, logging, caching, retry), (4) Functional programming - pure functions, immutable data, higher-order functions, (5) Proxy pattern - transparently intercept/modify requests/responses. Setup once: const api = axios.create({baseURL}); api.interceptors.request.use(config => {config.headers.Authorization = Bearer ${token}; return config;}). Benefits all requests automatically. Pattern implementations: Decorator (interceptors add behavior), Chain of Responsibility (interceptor chain), Strategy (configurable adapters), Template Method (axios.create() base config), Adapter (transformRequest/transformResponse). Code cleanliness: No repetitive token injection/error handling per component, centralized logic. Maintainability: Single source of truth for API logic, easier testing (mock interceptors), scalable architecture. 2025 best practices: Attach interceptors once at instance creation (not inside components - causes memory leaks), use functional approach, stack multiple interceptors. TypeScript: Full type safety for interceptors. Ecosystem: axios-cache-interceptor (caching), axios-retry (resilience). Alternative fetch: requires manual middleware implementation. Use Axios when: Need centralized request/response logic, complex interceptor chains, production-grade error handling.

    99% confidence