Files
skills/solid/references/design-patterns.md
Mathias d6a71e370e
Some checks failed
release / tag (push) Has been cancelled
chore: bootstrap skills library — 19 skills + installer + CI auto-tag
Phase 1 of mathias/skills extraction (infra#62 Track D — homelab
next-step plan addendum). Imports ~/dev/.skills/ verbatim (19 skill
dirs + SKILLS_INDEX.md) and adds the installation surface:

- Taskfile.yml — install / update / list / release / check targets
- install.sh — bootstrap installer for hosts without Task. Idempotent
  symlink wirer; default checkout at ~/.local/share/skills/ on every
  host; SKILLS_REF env var pins a tag (default: main).
- .gitea/workflows/release.yml — auto-tag every push to main by
  Bump-Type footer (major/minor/patch, default patch). Skipped when
  commit contains [skip-release].
- README — usage, versioning, contribution flow, secret-hygiene rule.

Phase 1 wires Claude Code only (~/.claude/skills/<name> global +
<repo>/.claude/skills/<name> per-repo). Phase 2 adds Crush, opencode,
antigravity, and gitea-resident agents (cobalt-dingo, agentsquad)
once their skill conventions are researched.

Public repo, markdown-only — no secrets, no client names. Verified
via pre-push grep before initial push.

[skip-release]
2026-05-24 14:59:54 +02:00

505 lines
10 KiB
Markdown

# Design Patterns
## What Are Design Patterns?
Reusable solutions to common design problems. A shared vocabulary for discussing design.
## WARNING: Don't Force Patterns
> "Let patterns emerge from refactoring, don't force them upfront."
Patterns should solve problems you HAVE, not problems you MIGHT have.
## When to Use Patterns
1. **You recognize the problem** - You've seen it before
2. **The pattern fits** - Not forcing it
3. **It simplifies** - Doesn't add unnecessary complexity
4. **Team understands it** - Shared knowledge
---
## Creational Patterns
### Singleton
**Purpose:** Ensure only one instance exists.
**When to use:** Global configuration, connection pools, logging.
**Warning:** Often overused. Consider dependency injection instead.
```typescript
class Logger {
private static instance: Logger;
private constructor() {}
static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
log(message: string): void { ... }
}
```
### Factory
**Purpose:** Create objects without specifying exact class.
**When to use:** Object creation logic is complex, or varies by type.
```typescript
interface Notification {
send(message: string): void;
}
class EmailNotification implements Notification { ... }
class SMSNotification implements Notification { ... }
class PushNotification implements Notification { ... }
class NotificationFactory {
create(type: 'email' | 'sms' | 'push'): Notification {
switch (type) {
case 'email': return new EmailNotification();
case 'sms': return new SMSNotification();
case 'push': return new PushNotification();
}
}
}
```
### Builder
**Purpose:** Construct complex objects step by step.
**When to use:** Objects with many optional parameters, test data creation.
```typescript
class UserBuilder {
private user: Partial<User> = {};
withName(name: string): UserBuilder {
this.user.name = name;
return this;
}
withEmail(email: string): UserBuilder {
this.user.email = email;
return this;
}
withAge(age: number): UserBuilder {
this.user.age = age;
return this;
}
build(): User {
return new User(
this.user.name!,
this.user.email!,
this.user.age
);
}
}
// Usage
const user = new UserBuilder()
.withName('Alice')
.withEmail('alice@example.com')
.build();
```
### Prototype
**Purpose:** Create new objects by cloning existing ones.
**When to use:** Object creation is expensive, or you need copies with slight variations.
```typescript
interface Prototype {
clone(): Prototype;
}
class Document implements Prototype {
constructor(
public title: string,
public content: string,
public metadata: Metadata
) {}
clone(): Document {
return new Document(
this.title,
this.content,
{ ...this.metadata }
);
}
}
```
---
## Structural Patterns
### Adapter
**Purpose:** Make incompatible interfaces work together.
**When to use:** Integrating third-party libraries, legacy code.
```typescript
// Third-party library with different interface
class OldPaymentAPI {
makePayment(cents: number): boolean { ... }
}
// Our interface
interface PaymentGateway {
charge(amount: Money): ChargeResult;
}
// Adapter
class OldPaymentAdapter implements PaymentGateway {
constructor(private oldAPI: OldPaymentAPI) {}
charge(amount: Money): ChargeResult {
const cents = amount.toCents();
const success = this.oldAPI.makePayment(cents);
return success ? ChargeResult.success() : ChargeResult.failed();
}
}
```
### Decorator
**Purpose:** Add behavior to objects dynamically.
**When to use:** Adding features without modifying existing code.
```typescript
interface Notifier {
send(message: string): void;
}
class EmailNotifier implements Notifier {
send(message: string): void {
console.log(`Email: ${message}`);
}
}
// Decorators
class SMSDecorator implements Notifier {
constructor(private wrapped: Notifier) {}
send(message: string): void {
this.wrapped.send(message);
console.log(`SMS: ${message}`);
}
}
class SlackDecorator implements Notifier {
constructor(private wrapped: Notifier) {}
send(message: string): void {
this.wrapped.send(message);
console.log(`Slack: ${message}`);
}
}
// Usage - compose behaviors
const notifier = new SlackDecorator(
new SMSDecorator(
new EmailNotifier()
)
);
notifier.send('Alert!'); // Sends to all three
```
### Proxy
**Purpose:** Control access to an object.
**When to use:** Lazy loading, access control, logging, caching.
```typescript
interface Image {
display(): void;
}
class RealImage implements Image {
constructor(private filename: string) {
this.loadFromDisk(); // Expensive
}
private loadFromDisk(): void { ... }
display(): void { ... }
}
// Lazy loading proxy
class ImageProxy implements Image {
private realImage: RealImage | null = null;
constructor(private filename: string) {}
display(): void {
if (!this.realImage) {
this.realImage = new RealImage(this.filename);
}
this.realImage.display();
}
}
```
### Composite
**Purpose:** Treat individual objects and compositions uniformly.
**When to use:** Tree structures, hierarchies (files/folders, UI components).
```typescript
interface Component {
getPrice(): number;
}
class Product implements Component {
constructor(private price: number) {}
getPrice(): number {
return this.price;
}
}
class Box implements Component {
private children: Component[] = [];
add(component: Component): void {
this.children.push(component);
}
getPrice(): number {
return this.children.reduce(
(sum, child) => sum + child.getPrice(),
0
);
}
}
// Usage
const smallBox = new Box();
smallBox.add(new Product(10));
smallBox.add(new Product(20));
const bigBox = new Box();
bigBox.add(smallBox);
bigBox.add(new Product(50));
console.log(bigBox.getPrice()); // 80
```
---
## Behavioral Patterns
### Strategy
**Purpose:** Define a family of algorithms, make them interchangeable.
**When to use:** Multiple ways to do something, switchable at runtime.
```typescript
interface PricingStrategy {
calculate(basePrice: number): number;
}
class RegularPricing implements PricingStrategy {
calculate(basePrice: number): number {
return basePrice;
}
}
class PremiumDiscount implements PricingStrategy {
calculate(basePrice: number): number {
return basePrice * 0.8; // 20% off
}
}
class BlackFriday implements PricingStrategy {
calculate(basePrice: number): number {
return basePrice * 0.5; // 50% off
}
}
class ShoppingCart {
constructor(private pricing: PricingStrategy) {}
calculateTotal(items: Item[]): number {
const base = items.reduce((sum, i) => sum + i.price, 0);
return this.pricing.calculate(base);
}
}
```
### Observer
**Purpose:** Notify multiple objects about state changes.
**When to use:** Event systems, pub/sub, reactive updates.
```typescript
interface Observer {
update(event: Event): void;
}
class EventEmitter {
private observers: Observer[] = [];
subscribe(observer: Observer): void {
this.observers.push(observer);
}
unsubscribe(observer: Observer): void {
this.observers = this.observers.filter(o => o !== observer);
}
notify(event: Event): void {
this.observers.forEach(o => o.update(event));
}
}
// Usage
class OrderService extends EventEmitter {
placeOrder(order: Order): void {
// Process order...
this.notify({ type: 'ORDER_PLACED', order });
}
}
class EmailService implements Observer {
update(event: Event): void {
if (event.type === 'ORDER_PLACED') {
this.sendConfirmation(event.order);
}
}
}
```
### Template Method
**Purpose:** Define algorithm skeleton, let subclasses override steps.
**When to use:** Common algorithm with varying steps.
```typescript
abstract class DataExporter {
// Template method - defines the algorithm
export(data: Data[]): void {
this.validate(data);
const formatted = this.format(data);
this.write(formatted);
this.notify();
}
// Common steps
private validate(data: Data[]): void { ... }
private notify(): void { ... }
// Steps to override
protected abstract format(data: Data[]): string;
protected abstract write(content: string): void;
}
class CSVExporter extends DataExporter {
protected format(data: Data[]): string {
return data.map(d => d.toCSV()).join('\n');
}
protected write(content: string): void {
fs.writeFileSync('export.csv', content);
}
}
class JSONExporter extends DataExporter {
protected format(data: Data[]): string {
return JSON.stringify(data);
}
protected write(content: string): void {
fs.writeFileSync('export.json', content);
}
}
```
### Command
**Purpose:** Encapsulate a request as an object.
**When to use:** Undo/redo, queuing, logging actions.
```typescript
interface Command {
execute(): void;
undo(): void;
}
class AddItemCommand implements Command {
constructor(
private cart: Cart,
private item: Item
) {}
execute(): void {
this.cart.add(this.item);
}
undo(): void {
this.cart.remove(this.item);
}
}
class CommandHistory {
private history: Command[] = [];
execute(command: Command): void {
command.execute();
this.history.push(command);
}
undo(): void {
const command = this.history.pop();
command?.undo();
}
}
```
---
## Pattern Awareness
### The Four-Dimensional Lens
When analyzing new code/libraries, ask:
1. **What problem does it solve?** (Creational, Structural, Behavioral)
2. **What scope?** (Object-level, Class-level, System-level)
3. **When is it applied?** (Compile-time, Runtime)
4. **How coupled?** (Tight, Loose)
This helps recognize patterns even in unfamiliar code.
---
## Anti-Patterns to Avoid
| Anti-Pattern | Problem | Solution |
|--------------|---------|----------|
| **God Object** | Class does everything | Split by responsibility |
| **Spaghetti Code** | Tangled, no structure | Refactor to layers |
| **Golden Hammer** | Using one pattern for everything | Match pattern to problem |
| **Premature Optimization** | Optimizing before needed | YAGNI, profile first |
| **Copy-Paste Programming** | Duplication | Extract, Rule of Three |