From harness-claude
Attaches additional behaviors to objects at runtime by wrapping them in decorator objects. Useful for stacking cross-cutting concerns like logging, caching, validation, or retry without subclassing.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:gof-decorator-patternThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Attach additional behavior to objects at runtime by wrapping them in decorator objects.
Attach additional behavior to objects at runtime by wrapping them in decorator objects.
Classic structural decorator:
// Component interface
interface DataSource {
write(data: string): Promise<void>;
read(): Promise<string>;
}
// Concrete component
class FileDataSource implements DataSource {
constructor(private readonly path: string) {}
async write(data: string): Promise<void> {
// Write to file
console.log(`Writing to ${this.path}: ${data}`);
}
async read(): Promise<string> {
// Read from file
return `data from ${this.path}`;
}
}
// Base decorator — implements the interface and wraps a component
abstract class DataSourceDecorator implements DataSource {
constructor(protected readonly wrapped: DataSource) {}
async write(data: string): Promise<void> {
return this.wrapped.write(data);
}
async read(): Promise<string> {
return this.wrapped.read();
}
}
// Concrete decorator: encryption
class EncryptionDecorator extends DataSourceDecorator {
async write(data: string): Promise<void> {
const encrypted = Buffer.from(data).toString('base64'); // simplified
await this.wrapped.write(encrypted);
}
async read(): Promise<string> {
const data = await this.wrapped.read();
return Buffer.from(data, 'base64').toString('utf8'); // simplified
}
}
// Concrete decorator: compression
class CompressionDecorator extends DataSourceDecorator {
async write(data: string): Promise<void> {
const compressed = `[compressed:${data}]`; // simplified
await this.wrapped.write(compressed);
}
async read(): Promise<string> {
const data = await this.wrapped.read();
return data.replace(/^\[compressed:/, '').replace(/\]$/, '');
}
}
// Stack decorators in any order
const source: DataSource = new CompressionDecorator(
new EncryptionDecorator(new FileDataSource('/data/users.dat'))
);
await source.write('hello world');
Function-based decorator (idiomatic TypeScript for async functions):
type AsyncFn<T extends unknown[], R> = (...args: T) => Promise<R>;
// Retry decorator
function withRetry<T extends unknown[], R>(
fn: AsyncFn<T, R>,
maxAttempts = 3,
delayMs = 500
): AsyncFn<T, R> {
return async (...args: T): Promise<R> => {
let lastError: Error;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn(...args);
} catch (err) {
lastError = err as Error;
if (attempt < maxAttempts) {
await new Promise((r) => setTimeout(r, delayMs * attempt));
}
}
}
throw lastError!;
};
}
// Cache decorator
function withCache<T extends unknown[], R>(fn: AsyncFn<T, R>, ttlMs = 60_000): AsyncFn<T, R> {
const cache = new Map<string, { value: R; expiresAt: number }>();
return async (...args: T): Promise<R> => {
const key = JSON.stringify(args);
const cached = cache.get(key);
if (cached && Date.now() < cached.expiresAt) return cached.value;
const value = await fn(...args);
cache.set(key, { value, expiresAt: Date.now() + ttlMs });
return value;
};
}
// Compose decorators
const fetchUser = async (id: string): Promise<User> => {
return db.users.findOneOrFail(id);
};
const robustFetchUser = withCache(withRetry(fetchUser, 3), 30_000);
Decorator vs. Proxy: Both wrap an object. The Decorator adds behavior; the Proxy controls access. In practice the implementation is similar — the distinction is intent. Use Decorator for feature stacking (logging, caching), use Proxy for access control (authorization, lazy loading).
TypeScript class decorators vs. GOF Decorator pattern: TypeScript's @Decorator syntax is a different mechanism (metaprogramming on class metadata). The GOF Decorator is a runtime object-wrapping pattern. The GOF pattern works without experimentalDecorators and is preferred for production code.
Anti-patterns:
Middleware pipeline (alternative for many cross-cutting concerns):
type Middleware<T> = (value: T, next: () => Promise<T>) => Promise<T>;
async function pipeline<T>(value: T, middlewares: Middleware<T>[]): Promise<T> {
const run = async (index: number): Promise<T> => {
if (index >= middlewares.length) return value;
return middlewares[index](value, () => run(index + 1));
};
return run(0);
}
refactoring.guru/design-patterns/decorator
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeAdds runtime object responsibilities via wrapping (Decorator pattern). Applies to OOP codebases, especially Java I/O streams, Python decorators, and Django middleware.
Implements the Decorator pattern in JavaScript to dynamically extend object/function behavior without modifying source. Useful for composing cross-cutting concerns like logging, caching, or auth checks.
Generates Decorator pattern for PHP 8.4: interface, abstract decorator, concrete decorators (logging, caching, metrics, transactional), optional factory, and unit tests. For dynamic behavior without inheritance.