Skip to content
Back to skills

Gof Decorator Pattern

ASecurity

> Attach additional behavior to objects at runtime by wrapping them in decorator objects.

  • 20 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 11, 2026
developmenttypescriptgorefactoring

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 11, 2026

npx -y skills add Intense-Visions/harness-engineering --skill gof-decorator-pattern --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Gof Decorator Pattern?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Gof Decorator Pattern
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/intense-visions-gof-decorator-pattern/badge)](https://www.skillsdirectory.com/skills/intense-visions-gof-decorator-pattern)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# GOF Decorator Pattern

> Attach additional behavior to objects at runtime by wrapping them in decorator objects.

## When to Use

- You want to add behavior to individual objects without affecting other objects of the same class
- Subclassing would create a combinatorial explosion of classes for each feature combination
- You need to stack multiple behaviors in a flexible order at runtime
- The behavior you're adding is cross-cutting (logging, caching, validation, retry) and belongs outside core logic

## Instructions

**Classic structural decorator:**

```typescript
// 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):**

```typescript
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);
```

## Details

**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:**

- Decorator that reads but doesn't write — all interface methods must be delegated, even if the decorator only augments one
- Decorators with ordering dependencies — if the order matters, document it clearly or use a pipeline builder
- Too many decorator layers — consider a middleware pipeline instead when you have more than 3-4 stacked behaviors

**Middleware pipeline (alternative for many cross-cutting concerns):**

```typescript
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);
}
```

## Source

refactoring.guru/design-patterns/decorator

## Process

1. Read the instructions and examples in this document.
2. Apply the patterns to your implementation, adapting to your specific context.
3. Verify your implementation against the details and edge cases listed above.

## Harness Integration

- **Type:** knowledge — this skill is a reference document, not a procedural workflow.
- **No tools or state** — consumed as context by other skills and agents.

## Success Criteria

- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.

Files in this skill

  • SKILL.md5.7 KB
  • skill.yaml712 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…