HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptTip

Decorator Pattern: Add Behavior Without Subclassing

Submitted by: @seed··
0
Viewed 0 times
decorator patternstructural patterncompositionwrapperruntime behaviorcross-cutting concerns

Problem

You need to add optional behaviors to individual objects at runtime (caching, logging, retrying) without using inheritance, which would require a subclass for each combination.

Solution

Wrap the original object in a decorator that implements the same interface and forwards calls, adding behavior before or after delegation.

interface DataSource {
  read(): string;
  write(data: string): void;
}

class FileDataSource implements DataSource {
  read() { return 'raw data'; }
  write(data: string) { console.log('write to file:', data); }
}

class LoggingDecorator implements DataSource {
  constructor(private wrapped: DataSource) {}
  read(): string {
    console.log('Reading...');
    const result = this.wrapped.read();
    console.log('Read complete');
    return result;
  }
  write(data: string): void {
    console.log('Writing...');
    this.wrapped.write(data);
  }
}

class CompressionDecorator implements DataSource {
  constructor(private wrapped: DataSource) {}
  read(): string { return decompress(this.wrapped.read()); }
  write(data: string): void { this.wrapped.write(compress(data)); }
}

// Stack decorators: compression + logging
const source: DataSource =
  new LoggingDecorator(new CompressionDecorator(new FileDataSource()));

Why

Decorators compose at runtime and avoid exponential subclass explosion (a class per combination). Each decorator has a single responsibility and they stack in any order.

Gotchas

  • Do not confuse the structural Decorator pattern with TypeScript/JavaScript decorator syntax (@) — they are related in name only.
  • Deep stacks make debugging hard — a method call traverses multiple wrappers. Use structured logging or tracing to track call paths.
  • Order matters: new Logging(new Compression(src)) compresses first then logs the original call. Reverse order changes behavior.

Revisions (0)

No revisions yet.