patterntypescriptTip
Decorator Pattern: Add Behavior Without Subclassing
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.