feat(skills): add design-patterns analyzer skill
Comprehensive GoF design patterns analyzer with stack-aware suggestions. **Features**: - Detects 23 GoF patterns (Creational, Structural, Behavioral) - Stack detection (React, Angular, NestJS, Vue, Express, RxJS, Redux, ORMs) - Code smell detection with pattern suggestions - Quality evaluation (5 criteria scoring) - Prefers stack-native alternatives (e.g., React Context over Singleton) **Structure**: - 9 files: SKILL.md + reference docs + detection rules + evaluation checklists - 3 operating modes: Detection, Suggestion, Evaluation - Pattern-specific documentation for all 23 GoF patterns **Documentation**: - Added comprehensive example in guide section 5.4 - Updated examples/README.md with skill entry - Updated template count: 65 → 66 **Use cases**: - Analyze existing patterns in codebase - Suggest refactoring with stack-native patterns - Evaluate pattern implementation quality Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
936f8dd564
commit
fcc6f2dba3
12 changed files with 5842 additions and 3 deletions
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/FlorianBruniaux/claude-code-ultimate-guide/stargazers"><img src="https://img.shields.io/github/stars/FlorianBruniaux/claude-code-ultimate-guide?style=for-the-badge" alt="Stars"/></a>
|
<a href="https://github.com/FlorianBruniaux/claude-code-ultimate-guide/stargazers"><img src="https://img.shields.io/github/stars/FlorianBruniaux/claude-code-ultimate-guide?style=for-the-badge" alt="Stars"/></a>
|
||||||
<a href="./examples/"><img src="https://img.shields.io/badge/Templates-65-green?style=for-the-badge" alt="Templates"/></a>
|
<a href="./examples/"><img src="https://img.shields.io/badge/Templates-66-green?style=for-the-badge" alt="Templates"/></a>
|
||||||
<a href="./quiz/"><img src="https://img.shields.io/badge/Quiz-227_questions-orange?style=for-the-badge" alt="Quiz"/></a>
|
<a href="./quiz/"><img src="https://img.shields.io/badge/Quiz-227_questions-orange?style=for-the-badge" alt="Quiz"/></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ Save as `CLAUDE.md` in your project root. Claude reads it automatically.
|
||||||
|
|
||||||
**The problem**: Awesome-lists give links, not learning paths. Official docs are dense. Tutorials get outdated in weeks.
|
**The problem**: Awesome-lists give links, not learning paths. Official docs are dense. Tutorials get outdated in weeks.
|
||||||
|
|
||||||
**This guide**: Structured learning path with 69 copy-paste templates, from first install to advanced workflows.
|
**This guide**: Structured learning path with 66 copy-paste templates, from first install to advanced workflows.
|
||||||
|
|
||||||
**Reading time**: Quick Start ~15 min. Full guide ~3 hours (most read by section).
|
**Reading time**: Quick Start ~15 min. Full guide ~3 hours (most read by section).
|
||||||
|
|
||||||
|
|
@ -196,7 +196,7 @@ claude-code-ultimate-guide/
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>Examples Library</strong> (65 templates)</summary>
|
<summary><strong>Examples Library</strong> (66 templates)</summary>
|
||||||
|
|
||||||
**Agents** (6): [code-reviewer](./examples/agents/code-reviewer.md), [test-writer](./examples/agents/test-writer.md), [security-auditor](./examples/agents/security-auditor.md), [refactoring-specialist](./examples/agents/refactoring-specialist.md), [output-evaluator](./examples/agents/output-evaluator.md), [devops-sre](./examples/agents/devops-sre.md) ⭐
|
**Agents** (6): [code-reviewer](./examples/agents/code-reviewer.md), [test-writer](./examples/agents/test-writer.md), [security-auditor](./examples/agents/security-auditor.md), [refactoring-specialist](./examples/agents/refactoring-specialist.md), [output-evaluator](./examples/agents/output-evaluator.md), [devops-sre](./examples/agents/devops-sre.md) ⭐
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ Ready-to-use templates for Claude Code configuration.
|
||||||
### Skills
|
### Skills
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
|
| [design-patterns/](./skills/design-patterns/) | Detect and analyze GoF design patterns with stack-aware suggestions ⭐ |
|
||||||
| [tdd-workflow.md](./skills/tdd-workflow.md) | Test-Driven Development process |
|
| [tdd-workflow.md](./skills/tdd-workflow.md) | Test-Driven Development process |
|
||||||
| [security-checklist.md](./skills/security-checklist.md) | OWASP Top 10 security checks |
|
| [security-checklist.md](./skills/security-checklist.md) | OWASP Top 10 security checks |
|
||||||
| [pdf-generator.md](./skills/pdf-generator.md) | Professional PDF generation (Quarto/Typst) |
|
| [pdf-generator.md](./skills/pdf-generator.md) | Professional PDF generation (Quarto/Typst) |
|
||||||
|
|
|
||||||
565
examples/skills/design-patterns/SKILL.md
Normal file
565
examples/skills/design-patterns/SKILL.md
Normal file
|
|
@ -0,0 +1,565 @@
|
||||||
|
---
|
||||||
|
name: design-patterns
|
||||||
|
description: Analyze codebase for GoF design patterns - detection, suggestions, evaluation with stack-aware adaptations
|
||||||
|
allowed-tools: Read, Grep, Glob, mcp__grepai__grepai_search
|
||||||
|
context: fork
|
||||||
|
agent: specialist
|
||||||
|
---
|
||||||
|
|
||||||
|
# Design Patterns Analyzer Skill
|
||||||
|
|
||||||
|
**Purpose**: Detect, suggest, and evaluate Gang of Four (GoF) design patterns in TypeScript/JavaScript codebases with stack-aware adaptations.
|
||||||
|
|
||||||
|
## Core Capabilities
|
||||||
|
|
||||||
|
1. **Stack Detection**: Identify primary framework/library (React, Angular, NestJS, Vue, Express, RxJS, Redux, ORMs)
|
||||||
|
2. **Pattern Detection**: Find existing implementations of 23 GoF patterns
|
||||||
|
3. **Smart Suggestions**: Recommend patterns to fix code smells, using stack-native idioms when available
|
||||||
|
4. **Quality Evaluation**: Assess pattern implementation quality against best practices
|
||||||
|
|
||||||
|
## Operating Modes
|
||||||
|
|
||||||
|
### Mode 1: Detection
|
||||||
|
|
||||||
|
**Trigger**: User requests pattern detection or analysis
|
||||||
|
**Output**: JSON report of patterns found with confidence scores and stack context
|
||||||
|
|
||||||
|
**Workflow**:
|
||||||
|
```
|
||||||
|
1. Stack Detection (package.json, tsconfig.json, framework files)
|
||||||
|
2. Pattern Search (Glob for candidates → Grep for signatures → Read for validation)
|
||||||
|
3. Classification (native to stack vs custom implementations)
|
||||||
|
4. Confidence Scoring (0.0-1.0 based on detection rules)
|
||||||
|
5. JSON Report Generation
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example invocation**:
|
||||||
|
```
|
||||||
|
/design-patterns detect src/
|
||||||
|
/design-patterns analyze --format=json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mode 2: Suggestion
|
||||||
|
|
||||||
|
**Trigger**: User requests pattern suggestions or refactoring advice
|
||||||
|
**Output**: Markdown report with prioritized suggestions and stack-adapted examples
|
||||||
|
|
||||||
|
**Workflow**:
|
||||||
|
```
|
||||||
|
1. Code Smell Detection (switch statements, long parameter lists, global state, etc.)
|
||||||
|
2. Pattern Matching (map smell → applicable patterns)
|
||||||
|
3. Stack Adaptation (prefer native framework patterns over custom implementations)
|
||||||
|
4. Priority Ranking (impact × feasibility)
|
||||||
|
5. Markdown Report with Code Examples
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example invocation**:
|
||||||
|
```
|
||||||
|
/design-patterns suggest src/payment/
|
||||||
|
/design-patterns refactor --focus=creational
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mode 3: Evaluation
|
||||||
|
|
||||||
|
**Trigger**: User requests pattern quality assessment
|
||||||
|
**Output**: JSON report with scores per evaluation criterion
|
||||||
|
|
||||||
|
**Workflow**:
|
||||||
|
```
|
||||||
|
1. Pattern Identification (which pattern is implemented)
|
||||||
|
2. Criteria Assessment (correctness, testability, SOLID compliance, documentation)
|
||||||
|
3. Issue Detection (common mistakes, anti-patterns)
|
||||||
|
4. Scoring (0-10 per criterion)
|
||||||
|
5. JSON Report with Recommendations
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example invocation**:
|
||||||
|
```
|
||||||
|
/design-patterns evaluate src/services/singleton.ts
|
||||||
|
/design-patterns quality --pattern=observer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Methodology
|
||||||
|
|
||||||
|
### Phase 1: Stack Detection
|
||||||
|
|
||||||
|
**Sources** (in priority order):
|
||||||
|
1. `package.json` → Check dependencies and devDependencies
|
||||||
|
2. Framework-specific files → `angular.json`, `next.config.*`, `nest-cli.json`, `vite.config.*`
|
||||||
|
3. `tsconfig.json` → Check compilerOptions, paths, lib
|
||||||
|
4. File extensions → `*.jsx`, `*.tsx`, `*.vue` presence
|
||||||
|
|
||||||
|
**Detection Rules** (from `signatures/stack-patterns.yaml`):
|
||||||
|
- React: `react` in deps + `*.jsx/*.tsx` files
|
||||||
|
- Angular: `@angular/core` + `angular.json`
|
||||||
|
- NestJS: `@nestjs/core` + `nest-cli.json`
|
||||||
|
- Vue: `vue` v3+ + `*.vue` files
|
||||||
|
- Express: `express` in deps + `app.use` patterns
|
||||||
|
- RxJS: `rxjs` in deps + Observable usage
|
||||||
|
- Redux/Zustand: `redux`/`zustand` in deps + store patterns
|
||||||
|
- Prisma/TypeORM: `prisma`/`typeorm` in deps + schema files
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"stack_detected": {
|
||||||
|
"primary": "react",
|
||||||
|
"version": "18.2.0",
|
||||||
|
"secondary": ["typescript", "zustand", "prisma"],
|
||||||
|
"detection_sources": ["package.json", "tsconfig.json", "37 *.tsx files"],
|
||||||
|
"confidence": 0.95
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Pattern Detection
|
||||||
|
|
||||||
|
**Search Strategy**:
|
||||||
|
1. **Glob Phase**: Find candidate files by naming convention
|
||||||
|
- `*Singleton*.ts`, `*Factory*.ts`, `*Strategy*.ts`, `*Observer*.ts`, etc.
|
||||||
|
- `*Manager*.ts`, `*Builder*.ts`, `*Adapter*.ts`, `*Proxy*.ts`, etc.
|
||||||
|
|
||||||
|
2. **Grep Phase**: Search for pattern signatures (from `signatures/detection-rules.yaml`)
|
||||||
|
- Primary signals: `private constructor`, `static getInstance()`, `subscribe()`, `createXxx()`, etc.
|
||||||
|
- Secondary signals: Interface naming, delegation patterns, method signatures
|
||||||
|
|
||||||
|
3. **Read Phase**: Validate pattern structure
|
||||||
|
- Parse class/interface definitions
|
||||||
|
- Verify relationships (inheritance, composition, delegation)
|
||||||
|
- Check for complete pattern implementation vs partial usage
|
||||||
|
|
||||||
|
**Confidence Scoring**:
|
||||||
|
- 0.9-1.0: All primary + secondary signals present, structure matches exactly
|
||||||
|
- 0.7-0.89: All primary signals + some secondary, minor deviations
|
||||||
|
- 0.5-0.69: Primary signals present, missing secondary validation
|
||||||
|
- 0.3-0.49: Naming convention matches, weak structural evidence
|
||||||
|
- 0.0-0.29: Insufficient evidence, likely false positive
|
||||||
|
|
||||||
|
**Classification**:
|
||||||
|
- `native`: Pattern implemented using stack-native features (React Context, Angular Services, NestJS Guards, etc.)
|
||||||
|
- `custom`: Manual TypeScript implementation
|
||||||
|
- `library`: Third-party library providing pattern (RxJS Subject, Redux Store, etc.)
|
||||||
|
|
||||||
|
### Phase 3: Code Smell Detection
|
||||||
|
|
||||||
|
**Target Smells** (from `signatures/code-smells.yaml`):
|
||||||
|
1. **Switch on Type** → Strategy/Factory pattern
|
||||||
|
2. **Long Parameter List (>4)** → Builder pattern
|
||||||
|
3. **Global State Access** → Singleton (or preferably DI)
|
||||||
|
4. **Duplicated Conditionals on State** → State pattern
|
||||||
|
5. **Scattered Notification Logic** → Observer pattern
|
||||||
|
6. **Complex Object Creation** → Factory/Abstract Factory
|
||||||
|
7. **Tight Coupling to Concrete Classes** → Adapter/Bridge
|
||||||
|
8. **Repetitive Interface Conversions** → Adapter pattern
|
||||||
|
9. **Deep Nesting for Feature Addition** → Decorator pattern
|
||||||
|
10. **Large Class with Many Responsibilities** → Facade pattern
|
||||||
|
|
||||||
|
**Detection Heuristics**:
|
||||||
|
- Grep for `switch (.*type)`, `switch (.*kind)`, `switch (.*mode)`
|
||||||
|
- Count function parameters: `function \w+\([^)]{60,}\)` (approximation for >4 params)
|
||||||
|
- Search for global access: `window\.`, `global\.`, `process\.env\.\w+` (not in config files)
|
||||||
|
- Find state conditionals: `if.*state.*===.*&&.*if.*state.*===`
|
||||||
|
- Find notification patterns: `forEach.*notify`, `map.*\.emit\(`
|
||||||
|
|
||||||
|
### Phase 4: Stack-Aware Suggestions
|
||||||
|
|
||||||
|
**Adaptation Logic** (from `signatures/stack-patterns.yaml`):
|
||||||
|
|
||||||
|
```
|
||||||
|
IF pattern_detected == "custom" AND stack_has_native_equivalent:
|
||||||
|
SUGGEST: "Use stack-native pattern instead"
|
||||||
|
PROVIDE: Side-by-side comparison (current vs recommended)
|
||||||
|
|
||||||
|
ELSE IF code_smell_detected AND pattern_missing:
|
||||||
|
IF stack_provides_pattern:
|
||||||
|
SUGGEST: Stack-native implementation with examples
|
||||||
|
ELSE:
|
||||||
|
SUGGEST: Custom TypeScript implementation with best practices
|
||||||
|
|
||||||
|
ELSE IF pattern_implemented_incorrectly:
|
||||||
|
PROVIDE: Refactoring steps to fix anti-patterns
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Adaptations**:
|
||||||
|
|
||||||
|
| Pattern | Stack | Native Alternative | Recommendation |
|
||||||
|
|---------|-------|-------------------|----------------|
|
||||||
|
| Singleton | React | Context API + Provider | Use `createContext()` instead of `getInstance()` |
|
||||||
|
| Observer | Angular | RxJS Subject/BehaviorSubject | Use built-in Observables, not custom implementation |
|
||||||
|
| Decorator | NestJS | @Injectable() decorators + Interceptors | Use framework interceptors |
|
||||||
|
| Strategy | Vue 3 | Composition API composables | Use `ref()` + composables instead of classes |
|
||||||
|
| Chain of Responsibility | Express | Middleware (`app.use()`) | Use Express middleware chain |
|
||||||
|
| Command | Redux | Action creators + reducers | Use Redux actions, not custom command objects |
|
||||||
|
|
||||||
|
### Phase 5: Quality Evaluation
|
||||||
|
|
||||||
|
**Criteria** (from `checklists/pattern-evaluation.md`):
|
||||||
|
1. **Correctness (0-10)**: Does it match the canonical pattern structure?
|
||||||
|
2. **Testability (0-10)**: Can dependencies be mocked/stubbed easily?
|
||||||
|
3. **Single Responsibility (0-10)**: Does it do one thing only?
|
||||||
|
4. **Open/Closed Principle (0-10)**: Extensible without modification?
|
||||||
|
5. **Documentation (0-10)**: Clear intent, descriptive naming?
|
||||||
|
|
||||||
|
**Scoring Guidelines**:
|
||||||
|
- 9-10: Exemplary, reference-quality implementation
|
||||||
|
- 7-8: Good, minor improvements possible
|
||||||
|
- 5-6: Acceptable, notable issues to address
|
||||||
|
- 3-4: Problematic, significant refactoring needed
|
||||||
|
- 0-2: Incorrect or severely flawed
|
||||||
|
|
||||||
|
**Issue Detection**:
|
||||||
|
- Hard-coded dependencies (Singleton with new inside getInstance)
|
||||||
|
- God classes (too many responsibilities)
|
||||||
|
- Leaky abstractions (exposing internal structure)
|
||||||
|
- Missing error handling
|
||||||
|
- Poor naming (Strategy1, Strategy2 instead of descriptive names)
|
||||||
|
|
||||||
|
## Output Formats
|
||||||
|
|
||||||
|
### Detection Mode (JSON)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"scan_date": "2026-01-21T10:30:00Z",
|
||||||
|
"scope": "src/",
|
||||||
|
"files_scanned": 147,
|
||||||
|
"execution_time_ms": 2341
|
||||||
|
},
|
||||||
|
"stack_detected": {
|
||||||
|
"primary": "react",
|
||||||
|
"version": "18.2.0",
|
||||||
|
"secondary": ["typescript", "zustand", "prisma"],
|
||||||
|
"detection_sources": ["package.json", "tsconfig.json", "37 *.tsx files"],
|
||||||
|
"confidence": 0.95
|
||||||
|
},
|
||||||
|
"patterns_found": {
|
||||||
|
"singleton": [
|
||||||
|
{
|
||||||
|
"file": "src/lib/api-client.ts",
|
||||||
|
"lines": "5-28",
|
||||||
|
"confidence": 0.85,
|
||||||
|
"type": "custom",
|
||||||
|
"signals": ["private constructor", "static getInstance", "private static instance"],
|
||||||
|
"note": "Consider using React Context instead for better testability"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"observer": [
|
||||||
|
{
|
||||||
|
"file": "src/hooks/useAuth.ts",
|
||||||
|
"lines": "12-45",
|
||||||
|
"confidence": 0.92,
|
||||||
|
"type": "native",
|
||||||
|
"implementation": "React useState + useEffect",
|
||||||
|
"note": "Correctly using React's built-in observer pattern"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factory": [
|
||||||
|
{
|
||||||
|
"file": "src/services/notification-factory.ts",
|
||||||
|
"lines": "8-67",
|
||||||
|
"confidence": 0.78,
|
||||||
|
"type": "custom",
|
||||||
|
"signals": ["createNotification method", "type discrimination", "returns interface"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"total_patterns": 7,
|
||||||
|
"native_to_stack": 4,
|
||||||
|
"custom_implementations": 3,
|
||||||
|
"by_category": {
|
||||||
|
"creational": 2,
|
||||||
|
"structural": 3,
|
||||||
|
"behavioral": 2
|
||||||
|
},
|
||||||
|
"by_confidence": {
|
||||||
|
"high": 5,
|
||||||
|
"medium": 2,
|
||||||
|
"low": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"recommendations": [
|
||||||
|
"Consider replacing custom Singleton (api-client.ts) with React Context for better DI",
|
||||||
|
"Review Factory pattern (notification-factory.ts) - could be simplified with strategy pattern"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Suggestion Mode (Markdown)
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Design Pattern Suggestions
|
||||||
|
|
||||||
|
**Scope**: `src/payment/`
|
||||||
|
**Stack**: React 18 + TypeScript + Stripe
|
||||||
|
**Date**: 2026-01-21
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## High Priority
|
||||||
|
|
||||||
|
### 1. Strategy Pattern → `src/payment/processor.ts:45-89`
|
||||||
|
|
||||||
|
**Code Smell**: Switch statement on payment type (4 cases, 78 lines)
|
||||||
|
|
||||||
|
**Current Implementation** (lines 52-87):
|
||||||
|
```typescript
|
||||||
|
switch (paymentType) {
|
||||||
|
case 'credit':
|
||||||
|
// 20 lines of credit card logic
|
||||||
|
break;
|
||||||
|
case 'paypal':
|
||||||
|
// 15 lines of PayPal logic
|
||||||
|
break;
|
||||||
|
case 'crypto':
|
||||||
|
// 18 lines of crypto logic
|
||||||
|
break;
|
||||||
|
case 'bank':
|
||||||
|
// 12 lines of bank transfer logic
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Recommended (React-adapted Strategy)**:
|
||||||
|
```typescript
|
||||||
|
// Define strategy interface
|
||||||
|
interface PaymentStrategy {
|
||||||
|
process: (amount: number) => Promise<PaymentResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom hooks as strategies
|
||||||
|
const useCreditPayment = (): PaymentStrategy => ({
|
||||||
|
process: async (amount) => { /* credit logic */ }
|
||||||
|
});
|
||||||
|
|
||||||
|
const usePaypalPayment = (): PaymentStrategy => ({
|
||||||
|
process: async (amount) => { /* PayPal logic */ }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Strategy selection hook
|
||||||
|
const usePaymentStrategy = (type: PaymentType): PaymentStrategy => {
|
||||||
|
const strategies = {
|
||||||
|
credit: useCreditPayment(),
|
||||||
|
paypal: usePaypalPayment(),
|
||||||
|
crypto: useCryptoPayment(),
|
||||||
|
bank: useBankPayment(),
|
||||||
|
};
|
||||||
|
return strategies[type];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Usage in component
|
||||||
|
const PaymentForm = ({ type }: Props) => {
|
||||||
|
const strategy = usePaymentStrategy(type);
|
||||||
|
const handlePay = () => strategy.process(amount);
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact**:
|
||||||
|
- **Complexity**: Reduces cyclomatic complexity from 12 to 2
|
||||||
|
- **Extensibility**: New payment methods = new hook, no modification to existing code
|
||||||
|
- **Testability**: Each strategy hook can be tested in isolation
|
||||||
|
- **Effort**: ~2 hours (extract logic into hooks, add tests)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Medium Priority
|
||||||
|
|
||||||
|
### 2. Observer Pattern → `src/cart/CartManager.ts:23-156`
|
||||||
|
|
||||||
|
**Code Smell**: Manual notification logic scattered across 8 methods
|
||||||
|
|
||||||
|
**Current**: Manual loops calling update functions
|
||||||
|
**Recommended**: Use Zustand store (already in dependencies)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Instead of custom observer:
|
||||||
|
import create from 'zustand';
|
||||||
|
|
||||||
|
interface CartStore {
|
||||||
|
items: CartItem[];
|
||||||
|
addItem: (item: CartItem) => void;
|
||||||
|
removeItem: (id: string) => void;
|
||||||
|
// Zustand automatically notifies subscribers
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCartStore = create<CartStore>((set) => ({
|
||||||
|
items: [],
|
||||||
|
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
|
||||||
|
removeItem: (id) => set((state) => ({ items: state.items.filter(i => i.id !== id) })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Components auto-subscribe:
|
||||||
|
const CartDisplay = () => {
|
||||||
|
const items = useCartStore((state) => state.items);
|
||||||
|
// Re-renders automatically on cart changes
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact**:
|
||||||
|
- **LOC**: Reduces from 156 to ~25 lines
|
||||||
|
- **Stack-native**: Uses existing Zustand dependency
|
||||||
|
- **Testability**: Zustand stores are easily tested
|
||||||
|
- **Effort**: ~1.5 hours
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- **Total suggestions**: 4
|
||||||
|
- **High priority**: 2 (Strategy, Observer)
|
||||||
|
- **Medium priority**: 2 (Builder, Facade)
|
||||||
|
- **Estimated total effort**: ~6 hours
|
||||||
|
- **Primary benefits**: Reduced complexity, improved testability, stack-native idioms
|
||||||
|
```
|
||||||
|
|
||||||
|
### Evaluation Mode (JSON)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"file": "src/services/config-singleton.ts",
|
||||||
|
"pattern": "singleton",
|
||||||
|
"lines": "5-34",
|
||||||
|
"scores": {
|
||||||
|
"correctness": 8,
|
||||||
|
"testability": 4,
|
||||||
|
"single_responsibility": 9,
|
||||||
|
"open_closed": 7,
|
||||||
|
"documentation": 6,
|
||||||
|
"overall": 6.8
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"correctness": {
|
||||||
|
"score": 8,
|
||||||
|
"rationale": "Implements singleton structure correctly with private constructor and static getInstance",
|
||||||
|
"issues": ["Missing thread-safety consideration (not critical in JS single-threaded context)"]
|
||||||
|
},
|
||||||
|
"testability": {
|
||||||
|
"score": 4,
|
||||||
|
"rationale": "Hard to mock or reset instance in tests",
|
||||||
|
"issues": [
|
||||||
|
"No reset method for test isolation",
|
||||||
|
"Static instance makes dependency injection impossible",
|
||||||
|
"Tests must run in specific order or share state"
|
||||||
|
],
|
||||||
|
"suggestions": [
|
||||||
|
"Add resetInstance() method for tests (with appropriate guards)",
|
||||||
|
"Consider using dependency injection instead"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"single_responsibility": {
|
||||||
|
"score": 9,
|
||||||
|
"rationale": "Focuses solely on configuration management",
|
||||||
|
"issues": []
|
||||||
|
},
|
||||||
|
"open_closed": {
|
||||||
|
"score": 7,
|
||||||
|
"rationale": "Configuration can be extended but requires modification for new sources",
|
||||||
|
"suggestions": ["Consider strategy pattern for configuration sources"]
|
||||||
|
},
|
||||||
|
"documentation": {
|
||||||
|
"score": 6,
|
||||||
|
"rationale": "Has JSDoc but missing rationale for singleton choice",
|
||||||
|
"suggestions": ["Document why singleton is chosen over DI", "Add usage examples"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"recommendations": [
|
||||||
|
{
|
||||||
|
"priority": "high",
|
||||||
|
"suggestion": "Add test-friendly reset mechanism or refactor to use DI",
|
||||||
|
"rationale": "Current implementation makes testing difficult"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"priority": "medium",
|
||||||
|
"suggestion": "Document singleton rationale in JSDoc",
|
||||||
|
"rationale": "Team members should understand why global state is necessary here"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Constraints & Guidelines
|
||||||
|
|
||||||
|
### Read-Only Analysis
|
||||||
|
- **No modifications**: This skill only analyzes and suggests, never modifies code
|
||||||
|
- **No file creation**: Does not generate refactored code files
|
||||||
|
- **User decision**: All suggestions require explicit user approval before implementation
|
||||||
|
|
||||||
|
### Language Focus
|
||||||
|
- **Primary**: TypeScript (`.ts`, `.tsx`)
|
||||||
|
- **Secondary**: JavaScript (`.js`, `.jsx`)
|
||||||
|
- **Exclusions**: Other languages (Python, Java, C#) not supported
|
||||||
|
|
||||||
|
### Pattern Coverage
|
||||||
|
- **Creational (5)**: Singleton, Factory Method, Abstract Factory, Builder, Prototype
|
||||||
|
- **Structural (7)**: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
|
||||||
|
- **Behavioral (11)**: Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor, Interpreter
|
||||||
|
|
||||||
|
### Performance Considerations
|
||||||
|
- **Large codebases (>500 files)**: Use `--scope` to limit scan to specific directories
|
||||||
|
- **Parallel search**: Grep searches run independently for each pattern
|
||||||
|
- **Caching**: Stack detection results cached per session to avoid redundant package.json reads
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Basic Detection
|
||||||
|
```bash
|
||||||
|
# Detect all patterns in src/
|
||||||
|
/design-patterns detect src/
|
||||||
|
|
||||||
|
# Detect only creational patterns
|
||||||
|
/design-patterns detect src/ --category=creational
|
||||||
|
|
||||||
|
# Focus on specific pattern
|
||||||
|
/design-patterns detect src/ --pattern=singleton
|
||||||
|
```
|
||||||
|
|
||||||
|
### Targeted Suggestions
|
||||||
|
```bash
|
||||||
|
# Get suggestions for payment module
|
||||||
|
/design-patterns suggest src/payment/
|
||||||
|
|
||||||
|
# Focus on specific smell
|
||||||
|
/design-patterns suggest src/ --smell=switch-on-type
|
||||||
|
|
||||||
|
# High priority only
|
||||||
|
/design-patterns suggest src/ --priority=high
|
||||||
|
```
|
||||||
|
|
||||||
|
### Quality Evaluation
|
||||||
|
```bash
|
||||||
|
# Evaluate specific file
|
||||||
|
/design-patterns evaluate src/services/api-client.ts
|
||||||
|
|
||||||
|
# Evaluate all singletons
|
||||||
|
/design-patterns evaluate src/ --pattern=singleton
|
||||||
|
|
||||||
|
# Full quality report
|
||||||
|
/design-patterns evaluate src/ --detailed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration with Other Skills
|
||||||
|
|
||||||
|
This skill can be inherited by:
|
||||||
|
- `refactoring-specialist.md` → Provides pattern knowledge for refactoring
|
||||||
|
- `code-reviewer.md` → Adds pattern detection to review process
|
||||||
|
- `architecture-advisor.md` → Informs architectural decisions with pattern usage
|
||||||
|
|
||||||
|
## Reference Files
|
||||||
|
|
||||||
|
- `reference/patterns-index.yaml` → Machine-readable index of 23 patterns with metadata
|
||||||
|
- `reference/creational.md` → Creational patterns documentation
|
||||||
|
- `reference/structural.md` → Structural patterns documentation
|
||||||
|
- `reference/behavioral.md` → Behavioral patterns documentation
|
||||||
|
- `signatures/detection-rules.yaml` → Regex patterns and heuristics for detection
|
||||||
|
- `signatures/code-smells.yaml` → Mapping from code smells to applicable patterns
|
||||||
|
- `signatures/stack-patterns.yaml` → Stack detection rules and native pattern equivalents
|
||||||
|
- `checklists/pattern-evaluation.md` → Quality evaluation criteria and scoring guidelines
|
||||||
|
|
||||||
|
## Version
|
||||||
|
|
||||||
|
**Skill Version**: 1.0.0
|
||||||
|
**Pattern Coverage**: 23 GoF patterns
|
||||||
|
**Supported Stacks**: 8 (React, Angular, NestJS, Vue, Express, RxJS, Redux/Zustand, ORMs)
|
||||||
|
**Last Updated**: 2026-01-21
|
||||||
521
examples/skills/design-patterns/checklists/pattern-evaluation.md
Normal file
521
examples/skills/design-patterns/checklists/pattern-evaluation.md
Normal file
|
|
@ -0,0 +1,521 @@
|
||||||
|
# Design Pattern Quality Evaluation Checklist
|
||||||
|
|
||||||
|
Systematic criteria for evaluating the quality of design pattern implementations.
|
||||||
|
|
||||||
|
## Evaluation Criteria
|
||||||
|
|
||||||
|
Each criterion is scored **0-10**, where:
|
||||||
|
- **9-10**: Exemplary, reference-quality implementation
|
||||||
|
- **7-8**: Good, minor improvements possible
|
||||||
|
- **5-6**: Acceptable, notable issues to address
|
||||||
|
- **3-4**: Problematic, significant refactoring needed
|
||||||
|
- **0-2**: Incorrect or severely flawed
|
||||||
|
|
||||||
|
**Overall Score** = Average of all criteria scores
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Correctness (0-10)
|
||||||
|
|
||||||
|
**Question**: Does the implementation correctly follow the canonical pattern structure?
|
||||||
|
|
||||||
|
### Scoring Guidelines
|
||||||
|
|
||||||
|
| Score | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| 9-10 | Perfect adherence to pattern structure, all roles present and correctly implemented |
|
||||||
|
| 7-8 | Minor deviations that don't compromise pattern intent |
|
||||||
|
| 5-6 | Some structural issues but pattern is recognizable and functional |
|
||||||
|
| 3-4 | Significant structural problems, pattern only partially implemented |
|
||||||
|
| 0-2 | Incorrect implementation, doesn't match pattern at all |
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
**Singleton**:
|
||||||
|
- [ ] Private constructor
|
||||||
|
- [ ] Static getInstance() method
|
||||||
|
- [ ] Private static instance field
|
||||||
|
- [ ] Thread-safe (if relevant)
|
||||||
|
- [ ] Returns same instance every time
|
||||||
|
|
||||||
|
**Observer**:
|
||||||
|
- [ ] Subject interface with attach/detach/notify
|
||||||
|
- [ ] Observer interface with update method
|
||||||
|
- [ ] Subject maintains list of observers
|
||||||
|
- [ ] Notify calls update on all observers
|
||||||
|
- [ ] Observers can be added/removed dynamically
|
||||||
|
|
||||||
|
**Strategy**:
|
||||||
|
- [ ] Strategy interface defining algorithm
|
||||||
|
- [ ] Context holds strategy reference
|
||||||
|
- [ ] Context delegates to strategy
|
||||||
|
- [ ] Strategies are interchangeable
|
||||||
|
- [ ] Client can set strategy at runtime
|
||||||
|
|
||||||
|
**Factory Method**:
|
||||||
|
- [ ] Factory method returns interface/abstract class
|
||||||
|
- [ ] Subclasses override factory method
|
||||||
|
- [ ] Client code depends on interface, not concrete classes
|
||||||
|
- [ ] Creation logic is encapsulated
|
||||||
|
|
||||||
|
**Decorator**:
|
||||||
|
- [ ] Decorator implements same interface as wrapped object
|
||||||
|
- [ ] Decorator holds reference to wrapped object
|
||||||
|
- [ ] Decorator delegates to wrapped object
|
||||||
|
- [ ] Can stack multiple decorators
|
||||||
|
- [ ] Maintains interface contract
|
||||||
|
|
||||||
|
### Common Mistakes (Deductions)
|
||||||
|
|
||||||
|
- **-2**: Missing key component (e.g., Singleton without private constructor)
|
||||||
|
- **-3**: Incorrect delegation (e.g., Decorator not calling wrapped object)
|
||||||
|
- **-4**: Breaking pattern invariants (e.g., Singleton returning different instances)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Testability (0-10)
|
||||||
|
|
||||||
|
**Question**: How easy is it to write unit tests for this implementation?
|
||||||
|
|
||||||
|
### Scoring Guidelines
|
||||||
|
|
||||||
|
| Score | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| 9-10 | Easily mockable, injectable dependencies, no global state |
|
||||||
|
| 7-8 | Testable with minor setup, some coupling exists |
|
||||||
|
| 5-6 | Requires significant test setup, moderate coupling |
|
||||||
|
| 3-4 | Hard to test, tight coupling, global state |
|
||||||
|
| 0-2 | Nearly impossible to test, static dependencies, no injection points |
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- [ ] Dependencies are injected (not created internally)
|
||||||
|
- [ ] Interfaces are used (can be mocked)
|
||||||
|
- [ ] No hard-coded dependencies
|
||||||
|
- [ ] No global state access (or minimal)
|
||||||
|
- [ ] Test isolation is possible (tests don't affect each other)
|
||||||
|
- [ ] No static methods that can't be mocked
|
||||||
|
- [ ] Side effects are minimized or controllable
|
||||||
|
|
||||||
|
### Red Flags (Deductions)
|
||||||
|
|
||||||
|
- **-2**: Using `getInstance()` instead of dependency injection
|
||||||
|
- **-2**: Hard-coded concrete class instantiation
|
||||||
|
- **-3**: Accessing global state (window, global, process.env in business logic)
|
||||||
|
- **-3**: Static methods with side effects
|
||||||
|
- **-4**: No way to inject test doubles
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
**Bad Testability (Score: 2/10)**:
|
||||||
|
```typescript
|
||||||
|
class PaymentService {
|
||||||
|
processPayment(amount: number) {
|
||||||
|
// Hard to test: creates dependency internally
|
||||||
|
const gateway = PaymentGateway.getInstance();
|
||||||
|
// Hard to test: accesses global config
|
||||||
|
const apiKey = process.env.PAYMENT_API_KEY;
|
||||||
|
return gateway.charge(amount, apiKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good Testability (Score: 9/10)**:
|
||||||
|
```typescript
|
||||||
|
class PaymentService {
|
||||||
|
constructor(
|
||||||
|
private gateway: IPaymentGateway,
|
||||||
|
private config: Config
|
||||||
|
) {}
|
||||||
|
|
||||||
|
processPayment(amount: number) {
|
||||||
|
const apiKey = this.config.getPaymentApiKey();
|
||||||
|
return this.gateway.charge(amount, apiKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Easy to test with mocks
|
||||||
|
const mockGateway = { charge: jest.fn() };
|
||||||
|
const mockConfig = { getPaymentApiKey: () => 'test-key' };
|
||||||
|
const service = new PaymentService(mockGateway, mockConfig);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Single Responsibility Principle (0-10)
|
||||||
|
|
||||||
|
**Question**: Does the component have one, clearly defined responsibility?
|
||||||
|
|
||||||
|
### Scoring Guidelines
|
||||||
|
|
||||||
|
| Score | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| 9-10 | Single, focused responsibility; class has one reason to change |
|
||||||
|
| 7-8 | Mostly focused, minor secondary concerns |
|
||||||
|
| 5-6 | Multiple related responsibilities |
|
||||||
|
| 3-4 | Several unrelated responsibilities |
|
||||||
|
| 0-2 | God class with many responsibilities |
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- [ ] Class/module has one clear purpose
|
||||||
|
- [ ] All methods relate to the primary responsibility
|
||||||
|
- [ ] Changing one requirement doesn't necessitate changing this class
|
||||||
|
- [ ] Class name clearly reflects its responsibility
|
||||||
|
- [ ] No "and" in class name or description (e.g., "UserManagerAndLogger" is bad)
|
||||||
|
|
||||||
|
### Red Flags (Deductions)
|
||||||
|
|
||||||
|
- **-2**: Class handles 2 distinct concerns
|
||||||
|
- **-3**: Class handles 3+ concerns
|
||||||
|
- **-4**: God class (>20 methods, >300 lines)
|
||||||
|
- **-1**: Methods unrelated to primary responsibility
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
**Poor SRP (Score: 3/10)**:
|
||||||
|
```typescript
|
||||||
|
class UserService {
|
||||||
|
createUser(data: UserData) { /* ... */ }
|
||||||
|
validateEmail(email: string) { /* ... */ }
|
||||||
|
sendWelcomeEmail(user: User) { /* ... */ }
|
||||||
|
logUserActivity(activity: string) { /* ... */ }
|
||||||
|
generateReport(userId: string) { /* ... */ }
|
||||||
|
// Too many responsibilities: creation, validation, email, logging, reporting
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good SRP (Score: 9/10)**:
|
||||||
|
```typescript
|
||||||
|
class UserService {
|
||||||
|
constructor(
|
||||||
|
private validator: UserValidator,
|
||||||
|
private emailService: EmailService,
|
||||||
|
private logger: Logger
|
||||||
|
) {}
|
||||||
|
|
||||||
|
createUser(data: UserData): User {
|
||||||
|
// Focuses only on user creation orchestration
|
||||||
|
this.validator.validate(data);
|
||||||
|
const user = new User(data);
|
||||||
|
this.emailService.sendWelcomeEmail(user);
|
||||||
|
this.logger.logActivity('user_created', user.id);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Open/Closed Principle (0-10)
|
||||||
|
|
||||||
|
**Question**: Can the component be extended without modifying its source code?
|
||||||
|
|
||||||
|
### Scoring Guidelines
|
||||||
|
|
||||||
|
| Score | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| 9-10 | Fully extensible via inheritance or composition, no modification needed |
|
||||||
|
| 7-8 | Mostly extensible, minor modifications might be needed |
|
||||||
|
| 5-6 | Some extension points exist but limited |
|
||||||
|
| 3-4 | Hard to extend, requires modification in multiple places |
|
||||||
|
| 0-2 | Closed for extension, must modify source code |
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- [ ] Uses interfaces or abstract classes
|
||||||
|
- [ ] New behavior can be added via new classes, not modifications
|
||||||
|
- [ ] Configuration or strategy pattern for varying behavior
|
||||||
|
- [ ] No switch statements on types (if adding new type requires modification)
|
||||||
|
- [ ] Dependency inversion (depends on abstractions)
|
||||||
|
|
||||||
|
### Red Flags (Deductions)
|
||||||
|
|
||||||
|
- **-2**: Switch on type (adding new type requires modification)
|
||||||
|
- **-3**: No interfaces (concrete dependencies everywhere)
|
||||||
|
- **-3**: Hard-coded behavior (no extension points)
|
||||||
|
- **-4**: Modifying existing methods is the only way to add features
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
**Closed for Extension (Score: 2/10)**:
|
||||||
|
```typescript
|
||||||
|
class PaymentProcessor {
|
||||||
|
process(type: string, amount: number) {
|
||||||
|
switch (type) {
|
||||||
|
case 'credit': return this.processCreditCard(amount);
|
||||||
|
case 'paypal': return this.processPaypal(amount);
|
||||||
|
// Adding crypto payment requires modifying this class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Open for Extension (Score: 9/10)**:
|
||||||
|
```typescript
|
||||||
|
interface PaymentStrategy {
|
||||||
|
process(amount: number): Promise<Receipt>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentProcessor {
|
||||||
|
constructor(private strategies: Map<string, PaymentStrategy>) {}
|
||||||
|
|
||||||
|
process(type: string, amount: number) {
|
||||||
|
const strategy = this.strategies.get(type);
|
||||||
|
if (!strategy) throw new Error(`Unknown payment type: ${type}`);
|
||||||
|
return strategy.process(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new payment method without modifying PaymentProcessor
|
||||||
|
class CryptoPaymentStrategy implements PaymentStrategy {
|
||||||
|
process(amount: number) { /* ... */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Documentation (0-10)
|
||||||
|
|
||||||
|
**Question**: Is the implementation well-documented with clear intent and usage?
|
||||||
|
|
||||||
|
### Scoring Guidelines
|
||||||
|
|
||||||
|
| Score | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| 9-10 | Comprehensive documentation: intent, usage, examples, edge cases |
|
||||||
|
| 7-8 | Good documentation, covers main use cases |
|
||||||
|
| 5-6 | Basic documentation, minimal but present |
|
||||||
|
| 3-4 | Sparse documentation, unclear intent |
|
||||||
|
| 0-2 | No documentation or misleading documentation |
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- [ ] Class/interface has JSDoc/TSDoc comment explaining purpose
|
||||||
|
- [ ] Pattern intent is documented ("This is a Singleton because...")
|
||||||
|
- [ ] Public methods have documentation
|
||||||
|
- [ ] Complex logic has inline comments
|
||||||
|
- [ ] Usage examples are provided (in README or comments)
|
||||||
|
- [ ] Invariants and constraints are documented
|
||||||
|
- [ ] Naming is self-documenting (clear, descriptive names)
|
||||||
|
|
||||||
|
### Red Flags (Deductions)
|
||||||
|
|
||||||
|
- **-2**: No class-level documentation
|
||||||
|
- **-2**: Public API methods undocumented
|
||||||
|
- **-3**: Cryptic naming (x, foo, temp, data)
|
||||||
|
- **-1**: Complex logic without explanation
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
**Poor Documentation (Score: 2/10)**:
|
||||||
|
```typescript
|
||||||
|
class S {
|
||||||
|
private static i: S;
|
||||||
|
private constructor() {}
|
||||||
|
static get() {
|
||||||
|
if (!S.i) S.i = new S();
|
||||||
|
return S.i;
|
||||||
|
}
|
||||||
|
do(x: any) { /* ... */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good Documentation (Score: 9/10)**:
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* Configuration service implemented as a Singleton to ensure
|
||||||
|
* all components share the same configuration state.
|
||||||
|
*
|
||||||
|
* Use `ConfigService.getInstance()` to access the singleton instance.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const config = ConfigService.getInstance();
|
||||||
|
* const apiUrl = config.get('API_URL');
|
||||||
|
*/
|
||||||
|
class ConfigService {
|
||||||
|
private static instance: ConfigService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor prevents direct instantiation.
|
||||||
|
* Use `getInstance()` instead.
|
||||||
|
*/
|
||||||
|
private constructor() {
|
||||||
|
// Load configuration from environment
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the singleton instance of ConfigService.
|
||||||
|
* Creates the instance on first call (lazy initialization).
|
||||||
|
*
|
||||||
|
* @returns The singleton ConfigService instance
|
||||||
|
*/
|
||||||
|
public static getInstance(): ConfigService {
|
||||||
|
if (!ConfigService.instance) {
|
||||||
|
ConfigService.instance = new ConfigService();
|
||||||
|
}
|
||||||
|
return ConfigService.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a configuration value by key.
|
||||||
|
*
|
||||||
|
* @param key - Configuration key
|
||||||
|
* @returns Configuration value or undefined if not found
|
||||||
|
*/
|
||||||
|
public get(key: string): string | undefined {
|
||||||
|
return process.env[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern-Specific Evaluation
|
||||||
|
|
||||||
|
### Singleton Specific
|
||||||
|
|
||||||
|
**Additional Checklist**:
|
||||||
|
- [ ] Lazy initialization (if appropriate)
|
||||||
|
- [ ] Thread-safety considered (less critical in JS)
|
||||||
|
- [ ] Subclassing is prevented or controlled
|
||||||
|
- [ ] No public constructor
|
||||||
|
- [ ] Reset mechanism for tests (or DI alternative mentioned)
|
||||||
|
|
||||||
|
**Deductions**:
|
||||||
|
- **-3**: Public constructor (defeats purpose)
|
||||||
|
- **-2**: Multiple getInstance() methods returning different instances
|
||||||
|
- **-2**: No consideration of test isolation
|
||||||
|
|
||||||
|
### Observer Specific
|
||||||
|
|
||||||
|
**Additional Checklist**:
|
||||||
|
- [ ] Observers can unsubscribe
|
||||||
|
- [ ] No memory leaks (observers are properly removed)
|
||||||
|
- [ ] Notification order is deterministic (if it matters)
|
||||||
|
- [ ] Observers don't depend on notification order
|
||||||
|
- [ ] Subject doesn't know concrete observer types
|
||||||
|
|
||||||
|
**Deductions**:
|
||||||
|
- **-3**: No unsubscribe mechanism (memory leak risk)
|
||||||
|
- **-2**: Subject depends on concrete observer types
|
||||||
|
- **-2**: Notification order matters but isn't guaranteed
|
||||||
|
|
||||||
|
### Strategy Specific
|
||||||
|
|
||||||
|
**Additional Checklist**:
|
||||||
|
- [ ] Strategies implement common interface
|
||||||
|
- [ ] Context doesn't depend on concrete strategies
|
||||||
|
- [ ] Strategies are interchangeable
|
||||||
|
- [ ] Strategy can be set at runtime
|
||||||
|
- [ ] Strategies don't share state (unless explicitly designed to)
|
||||||
|
|
||||||
|
**Deductions**:
|
||||||
|
- **-3**: Context depends on concrete strategies
|
||||||
|
- **-2**: Strategies are not truly interchangeable
|
||||||
|
- **-2**: No way to change strategy at runtime
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overall Assessment Formula
|
||||||
|
|
||||||
|
```
|
||||||
|
Overall Score = (
|
||||||
|
Correctness × 0.30 +
|
||||||
|
Testability × 0.25 +
|
||||||
|
Single Responsibility × 0.20 +
|
||||||
|
Open/Closed × 0.15 +
|
||||||
|
Documentation × 0.10
|
||||||
|
) / 5
|
||||||
|
```
|
||||||
|
|
||||||
|
**Weighted** because correctness is most important, followed by testability.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interpretation Guide
|
||||||
|
|
||||||
|
| Overall Score | Interpretation | Action |
|
||||||
|
|--------------|----------------|--------|
|
||||||
|
| 9.0 - 10.0 | Excellent | Reference-quality, minimal changes needed |
|
||||||
|
| 7.0 - 8.9 | Good | Minor improvements, production-ready |
|
||||||
|
| 5.0 - 6.9 | Acceptable | Notable issues, refactoring recommended |
|
||||||
|
| 3.0 - 4.9 | Poor | Significant problems, refactoring required |
|
||||||
|
| 0.0 - 2.9 | Critical | Fundamentally flawed, redesign needed |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example Evaluation Report
|
||||||
|
|
||||||
|
### Pattern: Singleton
|
||||||
|
**File**: `src/services/config-singleton.ts`
|
||||||
|
**Lines**: 5-34
|
||||||
|
|
||||||
|
#### Scores
|
||||||
|
|
||||||
|
| Criterion | Score | Rationale |
|
||||||
|
|-----------|-------|-----------|
|
||||||
|
| Correctness | 8/10 | Implements singleton correctly, minor: no thread-safety (not critical in JS) |
|
||||||
|
| Testability | 4/10 | Hard to mock, no reset mechanism, global state makes tests interdependent |
|
||||||
|
| Single Responsibility | 9/10 | Focuses solely on configuration management |
|
||||||
|
| Open/Closed | 7/10 | Can add new config keys, but config sources are hard-coded |
|
||||||
|
| Documentation | 6/10 | Has JSDoc but missing rationale for singleton choice |
|
||||||
|
|
||||||
|
**Overall Score**: **6.8/10** (Acceptable)
|
||||||
|
|
||||||
|
#### Issues Identified
|
||||||
|
|
||||||
|
1. **High Priority**: Add `resetInstance()` method for test isolation
|
||||||
|
- Current: Tests must run in specific order
|
||||||
|
- Fix: Add `public static resetInstance()` guarded by environment check
|
||||||
|
|
||||||
|
2. **Medium Priority**: Document singleton rationale
|
||||||
|
- Current: Unclear why global state is necessary
|
||||||
|
- Fix: Add JSDoc explaining choice (e.g., "Singleton ensures consistent config across all services")
|
||||||
|
|
||||||
|
3. **Low Priority**: Consider dependency injection alternative
|
||||||
|
- Current: Hard to test, tight coupling
|
||||||
|
- Recommendation: Evaluate using DI container or React Context
|
||||||
|
|
||||||
|
#### Recommendations
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Add test-friendly reset
|
||||||
|
public static resetInstance(): void {
|
||||||
|
if (process.env.NODE_ENV === 'test') {
|
||||||
|
ConfigService.instance = null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Better yet: refactor to DI
|
||||||
|
class ConfigService {
|
||||||
|
constructor(private envVars: EnvVars) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject in DI container or provider
|
||||||
|
const config = new ConfigService(process.env);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage in Skill
|
||||||
|
|
||||||
|
When the design-patterns skill runs in **Evaluation Mode**, it:
|
||||||
|
|
||||||
|
1. Identifies which pattern is implemented
|
||||||
|
2. Applies relevant checklist items
|
||||||
|
3. Scores each criterion (0-10)
|
||||||
|
4. Calculates weighted overall score
|
||||||
|
5. Generates detailed report with:
|
||||||
|
- Scores table
|
||||||
|
- Issues found (priority-ranked)
|
||||||
|
- Specific recommendations with code examples
|
||||||
|
- Stack-native alternatives if applicable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- *Clean Code* by Robert C. Martin (SOLID principles)
|
||||||
|
- *Refactoring: Improving the Design of Existing Code* by Martin Fowler
|
||||||
|
- *Design Patterns: Elements of Reusable Object-Oriented Software* (Gang of Four)
|
||||||
1032
examples/skills/design-patterns/reference/behavioral.md
Normal file
1032
examples/skills/design-patterns/reference/behavioral.md
Normal file
File diff suppressed because it is too large
Load diff
562
examples/skills/design-patterns/reference/creational.md
Normal file
562
examples/skills/design-patterns/reference/creational.md
Normal file
|
|
@ -0,0 +1,562 @@
|
||||||
|
# Creational Design Patterns
|
||||||
|
|
||||||
|
Patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation.
|
||||||
|
|
||||||
|
## Singleton
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Ensures a class has only one instance and provides a global point of access to it.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Exactly one instance of a class is needed (configuration, logging, database connection)
|
||||||
|
- [x] Controlled access to a single object is required
|
||||||
|
- [x] The instance should be extensible by subclassing
|
||||||
|
|
||||||
|
**Warning**: Often overused. Consider dependency injection or context-based alternatives first.
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
class Singleton {
|
||||||
|
private static instance: Singleton;
|
||||||
|
private constructor() {
|
||||||
|
// Private constructor prevents instantiation
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): Singleton {
|
||||||
|
if (!Singleton.instance) {
|
||||||
|
Singleton.instance = new Singleton();
|
||||||
|
}
|
||||||
|
return Singleton.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public someMethod(): void {
|
||||||
|
// Business logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const instance = Singleton.getInstance();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stack-Native Alternatives
|
||||||
|
|
||||||
|
**React**:
|
||||||
|
```typescript
|
||||||
|
// Instead of Singleton, use Context
|
||||||
|
const ConfigContext = createContext<Config>(defaultConfig);
|
||||||
|
|
||||||
|
export const ConfigProvider = ({ children }: Props) => {
|
||||||
|
const [config] = useState(() => loadConfig());
|
||||||
|
return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Angular**:
|
||||||
|
```typescript
|
||||||
|
// Injectable service (singleton by default)
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ConfigService {
|
||||||
|
// Automatically singleton via DI
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**NestJS**:
|
||||||
|
```typescript
|
||||||
|
@Injectable() // Default scope is SINGLETON
|
||||||
|
export class AppService {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- `private constructor`
|
||||||
|
- `static getInstance()` method
|
||||||
|
- `private static instance` field
|
||||||
|
- Lazy initialization check: `if (!instance)`
|
||||||
|
|
||||||
|
### Code Smells It Fixes
|
||||||
|
- **Global state access**: Provides controlled access instead of scattered global variables
|
||||||
|
- **Multiple instances of shared resource**: Ensures single database connection, config object, etc.
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Hard to test**: Static methods and global state make unit testing difficult
|
||||||
|
- *Solution*: Use dependency injection instead, or provide `resetInstance()` for tests
|
||||||
|
- **Thread-safety issues**: (Less relevant in JavaScript's single-threaded model, but important for Node.js workers)
|
||||||
|
- **Hidden dependencies**: Classes using `getInstance()` have hidden coupling
|
||||||
|
- **Violates Single Responsibility**: Often manages both instance creation and business logic
|
||||||
|
|
||||||
|
### Evaluation Criteria
|
||||||
|
- **Testability**: 3/10 (hard to mock, global state)
|
||||||
|
- **Thread-safety**: 7/10 (less critical in JS)
|
||||||
|
- **Extensibility**: 5/10 (subclassing is complex)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Factory Method
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Defines an interface for creating an object, but lets subclasses decide which class to instantiate.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Class cannot anticipate the type of objects it needs to create
|
||||||
|
- [x] Class wants its subclasses to specify the objects it creates
|
||||||
|
- [x] Centralize object creation logic to avoid duplication
|
||||||
|
- [x] Need to decouple object creation from usage
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Product interface
|
||||||
|
interface Product {
|
||||||
|
operation(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete products
|
||||||
|
class ConcreteProductA implements Product {
|
||||||
|
operation(): string {
|
||||||
|
return 'Product A';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConcreteProductB implements Product {
|
||||||
|
operation(): string {
|
||||||
|
return 'Product B';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creator (Factory)
|
||||||
|
abstract class Creator {
|
||||||
|
// Factory method
|
||||||
|
abstract createProduct(): Product;
|
||||||
|
|
||||||
|
// Business logic using the product
|
||||||
|
someOperation(): string {
|
||||||
|
const product = this.createProduct();
|
||||||
|
return `Creator: ${product.operation()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete creators
|
||||||
|
class CreatorA extends Creator {
|
||||||
|
createProduct(): Product {
|
||||||
|
return new ConcreteProductA();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreatorB extends Creator {
|
||||||
|
createProduct(): Product {
|
||||||
|
return new ConcreteProductB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const creator: Creator = new CreatorA();
|
||||||
|
console.log(creator.someOperation());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modern TypeScript Alternative
|
||||||
|
```typescript
|
||||||
|
// Simpler approach without inheritance
|
||||||
|
type ProductType = 'A' | 'B';
|
||||||
|
|
||||||
|
function createProduct(type: ProductType): Product {
|
||||||
|
switch (type) {
|
||||||
|
case 'A': return new ConcreteProductA();
|
||||||
|
case 'B': return new ConcreteProductB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Method named `create*()` returning interface/abstract class
|
||||||
|
- `abstract createProduct()` in base class
|
||||||
|
- Subclasses override factory method
|
||||||
|
- `switch` or `if-else` on type/kind parameter
|
||||||
|
|
||||||
|
### Code Smells It Fixes
|
||||||
|
- **Tight coupling to concrete classes**: Client code depends on interface, not implementation
|
||||||
|
- **Duplication of instantiation logic**: Centralized in factory method
|
||||||
|
- **Switch statements scattered**: Consolidated in one place
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Simple Factory confusion**: Factory Method uses inheritance; Simple Factory uses composition
|
||||||
|
- **Too many parameters**: Should create objects with default configuration
|
||||||
|
- **Forgetting to make factory method abstract**: Defeats the purpose of subclass specialization
|
||||||
|
|
||||||
|
### Evaluation Criteria
|
||||||
|
- **Testability**: 8/10 (easy to mock products)
|
||||||
|
- **Flexibility**: 9/10 (new products don't modify existing code)
|
||||||
|
- **Complexity**: 6/10 (adds inheritance hierarchy)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Abstract Factory
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] System should be independent of how its products are created
|
||||||
|
- [x] System should be configured with one of multiple families of products
|
||||||
|
- [x] Family of related product objects must be used together
|
||||||
|
- [x] You want to provide a library of products and reveal only interfaces
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Abstract products
|
||||||
|
interface AbstractProductA {
|
||||||
|
usefulFunctionA(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AbstractProductB {
|
||||||
|
usefulFunctionB(): string;
|
||||||
|
anotherFunctionB(collaborator: AbstractProductA): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete products - Family 1
|
||||||
|
class ConcreteProductA1 implements AbstractProductA {
|
||||||
|
usefulFunctionA(): string {
|
||||||
|
return 'Product A1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConcreteProductB1 implements AbstractProductB {
|
||||||
|
usefulFunctionB(): string {
|
||||||
|
return 'Product B1';
|
||||||
|
}
|
||||||
|
|
||||||
|
anotherFunctionB(collaborator: AbstractProductA): string {
|
||||||
|
return `B1 collaborating with ${collaborator.usefulFunctionA()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete products - Family 2
|
||||||
|
class ConcreteProductA2 implements AbstractProductA {
|
||||||
|
usefulFunctionA(): string {
|
||||||
|
return 'Product A2';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConcreteProductB2 implements AbstractProductB {
|
||||||
|
usefulFunctionB(): string {
|
||||||
|
return 'Product B2';
|
||||||
|
}
|
||||||
|
|
||||||
|
anotherFunctionB(collaborator: AbstractProductA): string {
|
||||||
|
return `B2 collaborating with ${collaborator.usefulFunctionA()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract factory
|
||||||
|
interface AbstractFactory {
|
||||||
|
createProductA(): AbstractProductA;
|
||||||
|
createProductB(): AbstractProductB;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete factories
|
||||||
|
class ConcreteFactory1 implements AbstractFactory {
|
||||||
|
createProductA(): AbstractProductA {
|
||||||
|
return new ConcreteProductA1();
|
||||||
|
}
|
||||||
|
|
||||||
|
createProductB(): AbstractProductB {
|
||||||
|
return new ConcreteProductB1();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConcreteFactory2 implements AbstractFactory {
|
||||||
|
createProductA(): AbstractProductA {
|
||||||
|
return new ConcreteProductA2();
|
||||||
|
}
|
||||||
|
|
||||||
|
createProductB(): AbstractProductB {
|
||||||
|
return new ConcreteProductB2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client code
|
||||||
|
function clientCode(factory: AbstractFactory) {
|
||||||
|
const productA = factory.createProductA();
|
||||||
|
const productB = factory.createProductB();
|
||||||
|
|
||||||
|
console.log(productB.anotherFunctionB(productA));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
clientCode(new ConcreteFactory1());
|
||||||
|
clientCode(new ConcreteFactory2());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Multiple `create*()` methods in factory interface
|
||||||
|
- Families of related products (e.g., Button + Checkbox for Windows/Mac)
|
||||||
|
- Factory implementations return different product families
|
||||||
|
- Interface with 2+ factory methods
|
||||||
|
|
||||||
|
### Code Smells It Fixes
|
||||||
|
- **Inconsistent product families**: Ensures compatible products are created together (Windows Button + Windows Checkbox, not mixed)
|
||||||
|
- **Scattered creation logic**: Centralizes creation of related objects
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Over-engineering**: Often too complex for simple scenarios; Factory Method may suffice
|
||||||
|
- **Rigid product families**: Adding new product types requires changing all factories
|
||||||
|
- **Confusion with Factory Method**: Abstract Factory creates families; Factory Method creates one product type
|
||||||
|
|
||||||
|
### Evaluation Criteria
|
||||||
|
- **Testability**: 8/10 (factories are easily mocked)
|
||||||
|
- **Consistency**: 10/10 (guarantees compatible products)
|
||||||
|
- **Complexity**: 4/10 (high complexity, many classes)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Builder
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Separates the construction of a complex object from its representation, allowing step-by-step construction.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Object has many optional parameters (>4)
|
||||||
|
- [x] Construction process should allow different representations
|
||||||
|
- [x] Need to construct complex objects step-by-step
|
||||||
|
- [x] Want to avoid "telescoping constructor" anti-pattern
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Product
|
||||||
|
class House {
|
||||||
|
public walls: string = '';
|
||||||
|
public doors: number = 0;
|
||||||
|
public windows: number = 0;
|
||||||
|
public roof: string = '';
|
||||||
|
public garage: boolean = false;
|
||||||
|
public pool: boolean = false;
|
||||||
|
|
||||||
|
public describe(): string {
|
||||||
|
return `House with ${this.walls} walls, ${this.doors} doors, ${this.windows} windows, ${this.roof} roof, garage: ${this.garage}, pool: ${this.pool}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builder
|
||||||
|
class HouseBuilder {
|
||||||
|
private house: House;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.house = new House();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setWalls(walls: string): this {
|
||||||
|
this.house.walls = walls;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setDoors(doors: number): this {
|
||||||
|
this.house.doors = doors;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setWindows(windows: number): this {
|
||||||
|
this.house.windows = windows;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setRoof(roof: string): this {
|
||||||
|
this.house.roof = roof;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addGarage(): this {
|
||||||
|
this.house.garage = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addPool(): this {
|
||||||
|
this.house.pool = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public build(): House {
|
||||||
|
const result = this.house;
|
||||||
|
this.house = new House(); // Reset for next build
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const house = new HouseBuilder()
|
||||||
|
.setWalls('brick')
|
||||||
|
.setDoors(2)
|
||||||
|
.setWindows(6)
|
||||||
|
.setRoof('tile')
|
||||||
|
.addGarage()
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modern TypeScript Alternative (Type-Safe Builder)
|
||||||
|
```typescript
|
||||||
|
// Progressive type safety: each step unlocks the next
|
||||||
|
type HouseBuilderState<
|
||||||
|
TWalls extends boolean = false,
|
||||||
|
TRoof extends boolean = false
|
||||||
|
> = {
|
||||||
|
setWalls: TWalls extends true ? never : (walls: string) => HouseBuilderState<true, TRoof>;
|
||||||
|
setRoof: TRoof extends true ? never : (roof: string) => HouseBuilderState<TWalls, true>;
|
||||||
|
build: TWalls extends true ? (TRoof extends true ? () => House : never) : never;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Method chaining (returns `this` or builder type)
|
||||||
|
- `build()` method returning final product
|
||||||
|
- `with*()` or `set*()` methods
|
||||||
|
- Optional fields being set incrementally
|
||||||
|
|
||||||
|
### Code Smells It Fixes
|
||||||
|
- **Telescoping constructor**: Constructor with many parameters
|
||||||
|
```typescript
|
||||||
|
// Bad
|
||||||
|
new House(walls, doors, windows, roof, garage, pool, garden, basement, ...);
|
||||||
|
|
||||||
|
// Good with Builder
|
||||||
|
new HouseBuilder().setWalls('brick').setRoof('tile').build();
|
||||||
|
```
|
||||||
|
- **Unclear parameter order**: Named methods make intent clear
|
||||||
|
- **Optional parameters complexity**: Builder handles optional features elegantly
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Mutable builder**: Reusing builder can lead to unexpected state
|
||||||
|
- *Solution*: Reset internal state after `build()`
|
||||||
|
- **Incomplete builder**: Not validating required fields in `build()`
|
||||||
|
- *Solution*: Use TypeScript types to enforce required steps
|
||||||
|
- **Too simple for the pattern**: If <4 parameters, constructor or object literal may be simpler
|
||||||
|
|
||||||
|
### Evaluation Criteria
|
||||||
|
- **Testability**: 9/10 (easy to create test fixtures)
|
||||||
|
- **Readability**: 10/10 (fluent interface is self-documenting)
|
||||||
|
- **Complexity**: 7/10 (adds builder class)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prototype
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Creates new objects by copying an existing object (prototype) rather than creating from scratch.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Object creation is expensive (complex initialization, database queries)
|
||||||
|
- [x] Need to avoid subclassing just to change initialization
|
||||||
|
- [x] System should be independent of how products are created
|
||||||
|
- [x] Classes to instantiate are specified at runtime
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Prototype interface
|
||||||
|
interface Prototype {
|
||||||
|
clone(): Prototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete prototype
|
||||||
|
class ConcretePrototype implements Prototype {
|
||||||
|
public field: number;
|
||||||
|
public complexObject: { data: string };
|
||||||
|
|
||||||
|
constructor(field: number, complexObject: { data: string }) {
|
||||||
|
this.field = field;
|
||||||
|
this.complexObject = complexObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shallow clone
|
||||||
|
public clone(): ConcretePrototype {
|
||||||
|
return Object.create(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deep clone
|
||||||
|
public deepClone(): ConcretePrototype {
|
||||||
|
return new ConcretePrototype(
|
||||||
|
this.field,
|
||||||
|
{ data: this.complexObject.data } // Clone nested objects
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const original = new ConcretePrototype(42, { data: 'important' });
|
||||||
|
const shallowCopy = original.clone();
|
||||||
|
const deepCopy = original.deepClone();
|
||||||
|
|
||||||
|
// Shallow copy shares nested objects
|
||||||
|
shallowCopy.complexObject.data = 'modified';
|
||||||
|
console.log(original.complexObject.data); // 'modified' (!)
|
||||||
|
|
||||||
|
// Deep copy is independent
|
||||||
|
deepCopy.field = 99;
|
||||||
|
console.log(original.field); // 42 (unchanged)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modern JavaScript Alternatives
|
||||||
|
```typescript
|
||||||
|
// Spread operator (shallow)
|
||||||
|
const copy1 = { ...original };
|
||||||
|
|
||||||
|
// Object.assign (shallow)
|
||||||
|
const copy2 = Object.assign({}, original);
|
||||||
|
|
||||||
|
// structuredClone (deep, modern browsers/Node 17+)
|
||||||
|
const copy3 = structuredClone(original);
|
||||||
|
|
||||||
|
// JSON (deep, but limited: no functions, undefined, etc.)
|
||||||
|
const copy4 = JSON.parse(JSON.stringify(original));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- `clone()` method
|
||||||
|
- `Object.create()`
|
||||||
|
- `structuredClone()`
|
||||||
|
- `JSON.parse(JSON.stringify())` pattern
|
||||||
|
- Spread operator `{ ...obj }`
|
||||||
|
|
||||||
|
### Code Smells It Fixes
|
||||||
|
- **Expensive initialization**: Clone instead of re-initializing
|
||||||
|
- **Complex object graphs**: Cloning preserves relationships
|
||||||
|
- **Runtime type specification**: Clone prototype instead of hardcoding types
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Shallow vs Deep clone confusion**: Shallow clone shares nested objects
|
||||||
|
```typescript
|
||||||
|
// Dangerous if nested objects are modified
|
||||||
|
const shallow = { ...original };
|
||||||
|
```
|
||||||
|
- **Circular references**: `JSON.stringify` fails on circular references
|
||||||
|
- *Solution*: Use `structuredClone()` or custom clone logic
|
||||||
|
- **Cloning methods/functions**: Some approaches lose methods
|
||||||
|
```typescript
|
||||||
|
JSON.parse(JSON.stringify(obj)); // Loses all methods!
|
||||||
|
```
|
||||||
|
- **Not cloning private state**: Ensure all necessary state is copied
|
||||||
|
|
||||||
|
### Evaluation Criteria
|
||||||
|
- **Performance**: 9/10 (faster than re-initialization)
|
||||||
|
- **Simplicity**: 7/10 (shallow vs deep cloning is tricky)
|
||||||
|
- **Reliability**: 6/10 (easy to get wrong with nested objects)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary Table
|
||||||
|
|
||||||
|
| Pattern | Complexity | Use Frequency | Main Benefit |
|
||||||
|
|---------|------------|---------------|--------------|
|
||||||
|
| Singleton | Low | High | Global access control |
|
||||||
|
| Factory Method | Medium | High | Decouples creation from usage |
|
||||||
|
| Abstract Factory | High | Medium | Consistent product families |
|
||||||
|
| Builder | Medium | High | Fluent construction of complex objects |
|
||||||
|
| Prototype | Low | Low | Efficient cloning |
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Prefer composition over inheritance**: Factory and Builder often better than Singleton
|
||||||
|
2. **Use stack-native alternatives**: React Context > Singleton, DI > getInstance()
|
||||||
|
3. **TypeScript leverage**: Use generics and type constraints for type-safe builders
|
||||||
|
4. **Test-friendly design**: Avoid Singleton; use dependency injection
|
||||||
|
5. **Simplicity first**: Don't use Abstract Factory when Factory Method suffices
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- *Design Patterns: Elements of Reusable Object-Oriented Software* (Gang of Four)
|
||||||
|
- *Effective TypeScript* by Dan Vanderkam
|
||||||
|
- [Refactoring Guru: Creational Patterns](https://refactoring.guru/design-patterns/creational-patterns)
|
||||||
308
examples/skills/design-patterns/reference/patterns-index.yaml
Normal file
308
examples/skills/design-patterns/reference/patterns-index.yaml
Normal file
|
|
@ -0,0 +1,308 @@
|
||||||
|
version: "1.0.0"
|
||||||
|
total_patterns: 23
|
||||||
|
description: Machine-readable index of Gang of Four design patterns for TypeScript/JavaScript
|
||||||
|
|
||||||
|
categories:
|
||||||
|
creational:
|
||||||
|
- singleton
|
||||||
|
- factory-method
|
||||||
|
- abstract-factory
|
||||||
|
- builder
|
||||||
|
- prototype
|
||||||
|
structural:
|
||||||
|
- adapter
|
||||||
|
- bridge
|
||||||
|
- composite
|
||||||
|
- decorator
|
||||||
|
- facade
|
||||||
|
- flyweight
|
||||||
|
- proxy
|
||||||
|
behavioral:
|
||||||
|
- chain-of-responsibility
|
||||||
|
- command
|
||||||
|
- iterator
|
||||||
|
- mediator
|
||||||
|
- memento
|
||||||
|
- observer
|
||||||
|
- state
|
||||||
|
- strategy
|
||||||
|
- template-method
|
||||||
|
- visitor
|
||||||
|
- interpreter
|
||||||
|
|
||||||
|
pattern_metadata:
|
||||||
|
# Creational Patterns
|
||||||
|
singleton:
|
||||||
|
difficulty: 1
|
||||||
|
use_frequency: high
|
||||||
|
detection_confidence: 0.85
|
||||||
|
typical_files: ["*Manager.ts", "*Service.ts", "*Client.ts", "*Config.ts"]
|
||||||
|
primary_signals: ["private constructor", "static getInstance", "private static instance"]
|
||||||
|
anti_patterns: ["hard to test", "global state", "tight coupling"]
|
||||||
|
related: [dependency-injection]
|
||||||
|
stack_native:
|
||||||
|
react: "Context API"
|
||||||
|
angular: "Injectable services"
|
||||||
|
nestjs: "@Injectable() decorator"
|
||||||
|
|
||||||
|
factory-method:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: high
|
||||||
|
detection_confidence: 0.75
|
||||||
|
typical_files: ["*Factory.ts", "*Creator.ts", "*Provider.ts"]
|
||||||
|
primary_signals: ["create* method", "returns interface/abstract class", "subclass decides instantiation"]
|
||||||
|
anti_patterns: ["simple factory confusion", "too many parameters"]
|
||||||
|
related: [abstract-factory, builder]
|
||||||
|
|
||||||
|
abstract-factory:
|
||||||
|
difficulty: 3
|
||||||
|
use_frequency: medium
|
||||||
|
detection_confidence: 0.70
|
||||||
|
typical_files: ["*Factory.ts", "*AbstractFactory.ts"]
|
||||||
|
primary_signals: ["multiple create* methods", "family of related objects", "interface for factory"]
|
||||||
|
anti_patterns: ["complexity overhead", "rigid product families"]
|
||||||
|
related: [factory-method, prototype]
|
||||||
|
|
||||||
|
builder:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: high
|
||||||
|
detection_confidence: 0.80
|
||||||
|
typical_files: ["*Builder.ts"]
|
||||||
|
primary_signals: ["fluent interface", "method chaining", "build() method", "step-by-step construction"]
|
||||||
|
anti_patterns: ["incomplete builders", "mutable builders"]
|
||||||
|
related: [factory-method]
|
||||||
|
common_use_cases: ["query builders", "request builders", "complex configuration"]
|
||||||
|
|
||||||
|
prototype:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: low
|
||||||
|
detection_confidence: 0.65
|
||||||
|
typical_files: ["*Prototype.ts"]
|
||||||
|
primary_signals: ["clone() method", "deep copy", "Object.create()"]
|
||||||
|
anti_patterns: ["shallow vs deep copy confusion", "circular references"]
|
||||||
|
related: [factory-method]
|
||||||
|
|
||||||
|
# Structural Patterns
|
||||||
|
adapter:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: high
|
||||||
|
detection_confidence: 0.75
|
||||||
|
typical_files: ["*Adapter.ts", "*Wrapper.ts"]
|
||||||
|
primary_signals: ["wraps incompatible interface", "delegates to adaptee", "converts interface"]
|
||||||
|
anti_patterns: ["two-way adapters", "adapter chains"]
|
||||||
|
related: [bridge, decorator, proxy]
|
||||||
|
common_use_cases: ["third-party library integration", "legacy code integration"]
|
||||||
|
|
||||||
|
bridge:
|
||||||
|
difficulty: 3
|
||||||
|
use_frequency: low
|
||||||
|
detection_confidence: 0.60
|
||||||
|
typical_files: ["*Bridge.ts", "*Abstraction.ts", "*Implementation.ts"]
|
||||||
|
primary_signals: ["abstraction has implementation reference", "two hierarchies", "runtime binding"]
|
||||||
|
anti_patterns: ["over-engineering simple scenarios"]
|
||||||
|
related: [adapter, strategy]
|
||||||
|
|
||||||
|
composite:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: medium
|
||||||
|
detection_confidence: 0.75
|
||||||
|
typical_files: ["*Component.ts", "*Composite.ts", "*Leaf.ts"]
|
||||||
|
primary_signals: ["tree structure", "uniform interface", "recursive composition", "children collection"]
|
||||||
|
anti_patterns: ["incorrect child management", "violating uniformity"]
|
||||||
|
related: [decorator, iterator]
|
||||||
|
common_use_cases: ["UI component trees", "file systems", "organization structures"]
|
||||||
|
|
||||||
|
decorator:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: high
|
||||||
|
detection_confidence: 0.70
|
||||||
|
typical_files: ["*Decorator.ts", "*Wrapper.ts"]
|
||||||
|
primary_signals: ["wraps same interface", "adds behavior", "delegates to wrapped", "stackable"]
|
||||||
|
anti_patterns: ["decorator explosion", "order dependency"]
|
||||||
|
related: [adapter, composite, proxy]
|
||||||
|
stack_native:
|
||||||
|
react: "Higher-Order Components (HOC)"
|
||||||
|
angular: "Directives"
|
||||||
|
nestjs: "Interceptors"
|
||||||
|
typescript: "Decorators (@)"
|
||||||
|
|
||||||
|
facade:
|
||||||
|
difficulty: 1
|
||||||
|
use_frequency: high
|
||||||
|
detection_confidence: 0.80
|
||||||
|
typical_files: ["*Facade.ts", "*API.ts", "*Interface.ts"]
|
||||||
|
primary_signals: ["simple interface to complex subsystem", "delegates to multiple classes"]
|
||||||
|
anti_patterns: ["god facade", "leaky abstraction"]
|
||||||
|
related: [adapter, mediator]
|
||||||
|
common_use_cases: ["API clients", "library wrappers", "subsystem interfaces"]
|
||||||
|
|
||||||
|
flyweight:
|
||||||
|
difficulty: 3
|
||||||
|
use_frequency: low
|
||||||
|
detection_confidence: 0.65
|
||||||
|
typical_files: ["*Flyweight.ts", "*Factory.ts"]
|
||||||
|
primary_signals: ["shared state", "intrinsic vs extrinsic state", "large number of objects", "factory for sharing"]
|
||||||
|
anti_patterns: ["premature optimization", "incorrect state separation"]
|
||||||
|
related: [singleton, factory-method]
|
||||||
|
|
||||||
|
proxy:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: medium
|
||||||
|
detection_confidence: 0.75
|
||||||
|
typical_files: ["*Proxy.ts"]
|
||||||
|
primary_signals: ["same interface as real subject", "controls access", "lazy initialization", "logging/caching"]
|
||||||
|
anti_patterns: ["proxy chains", "performance overhead"]
|
||||||
|
related: [adapter, decorator]
|
||||||
|
common_use_cases: ["lazy loading", "access control", "caching", "logging"]
|
||||||
|
stack_native:
|
||||||
|
javascript: "Proxy object"
|
||||||
|
|
||||||
|
# Behavioral Patterns
|
||||||
|
chain-of-responsibility:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: medium
|
||||||
|
detection_confidence: 0.70
|
||||||
|
typical_files: ["*Handler.ts", "*Middleware.ts"]
|
||||||
|
primary_signals: ["handler interface", "next handler reference", "handles or passes", "linked list structure"]
|
||||||
|
anti_patterns: ["unhandled requests", "broken chains"]
|
||||||
|
related: [command, decorator]
|
||||||
|
stack_native:
|
||||||
|
express: "Middleware chain (app.use)"
|
||||||
|
nestjs: "Guards, Interceptors, Pipes"
|
||||||
|
|
||||||
|
command:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: medium
|
||||||
|
detection_confidence: 0.75
|
||||||
|
typical_files: ["*Command.ts", "*Action.ts"]
|
||||||
|
primary_signals: ["execute() method", "encapsulates request", "receiver reference", "undo/redo support"]
|
||||||
|
anti_patterns: ["command explosion", "complex commands"]
|
||||||
|
related: [memento, chain-of-responsibility]
|
||||||
|
stack_native:
|
||||||
|
redux: "Action creators"
|
||||||
|
vuex: "Mutations"
|
||||||
|
|
||||||
|
iterator:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: medium
|
||||||
|
detection_confidence: 0.80
|
||||||
|
typical_files: ["*Iterator.ts"]
|
||||||
|
primary_signals: ["next() method", "hasNext()/done", "current element access", "sequential traversal"]
|
||||||
|
anti_patterns: ["modification during iteration"]
|
||||||
|
related: [composite, visitor]
|
||||||
|
stack_native:
|
||||||
|
javascript: "Iterators and generators (Symbol.iterator)"
|
||||||
|
typescript: "Iterable<T> interface"
|
||||||
|
|
||||||
|
mediator:
|
||||||
|
difficulty: 3
|
||||||
|
use_frequency: medium
|
||||||
|
detection_confidence: 0.65
|
||||||
|
typical_files: ["*Mediator.ts", "*Controller.ts"]
|
||||||
|
primary_signals: ["centralized communication", "colleagues reference mediator", "loose coupling"]
|
||||||
|
anti_patterns: ["god mediator", "tight coupling to mediator"]
|
||||||
|
related: [facade, observer]
|
||||||
|
stack_native:
|
||||||
|
react: "Context API"
|
||||||
|
angular: "Services with dependency injection"
|
||||||
|
|
||||||
|
memento:
|
||||||
|
difficulty: 3
|
||||||
|
use_frequency: low
|
||||||
|
detection_confidence: 0.60
|
||||||
|
typical_files: ["*Memento.ts", "*Snapshot.ts", "*Caretaker.ts"]
|
||||||
|
primary_signals: ["save state", "restore state", "encapsulated snapshot", "caretaker manages history"]
|
||||||
|
anti_patterns: ["large state snapshots", "exposing internals"]
|
||||||
|
related: [command]
|
||||||
|
common_use_cases: ["undo/redo", "transaction rollback", "state history"]
|
||||||
|
|
||||||
|
observer:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: very-high
|
||||||
|
detection_confidence: 0.85
|
||||||
|
typical_files: ["*Observer.ts", "*Subject.ts", "*Publisher.ts", "*Subscriber.ts"]
|
||||||
|
primary_signals: ["subscribe/unsubscribe", "notify/emit", "observers list", "one-to-many dependency"]
|
||||||
|
anti_patterns: ["memory leaks", "notification storms", "observer order dependency"]
|
||||||
|
related: [mediator, singleton]
|
||||||
|
stack_native:
|
||||||
|
javascript: "EventEmitter, EventTarget"
|
||||||
|
react: "useState, useEffect, Context"
|
||||||
|
angular: "RxJS Observables"
|
||||||
|
rxjs: "Subject, BehaviorSubject, ReplaySubject"
|
||||||
|
vue: "ref(), reactive(), watch()"
|
||||||
|
|
||||||
|
state:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: medium
|
||||||
|
detection_confidence: 0.70
|
||||||
|
typical_files: ["*State.ts", "*Context.ts"]
|
||||||
|
primary_signals: ["state interface", "context holds state", "state transitions", "behavior varies by state"]
|
||||||
|
anti_patterns: ["state explosion", "complex transitions"]
|
||||||
|
related: [strategy, flyweight]
|
||||||
|
stack_native:
|
||||||
|
react: "useState, useReducer"
|
||||||
|
redux: "Reducers"
|
||||||
|
xstate: "State machines"
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
difficulty: 1
|
||||||
|
use_frequency: high
|
||||||
|
detection_confidence: 0.80
|
||||||
|
typical_files: ["*Strategy.ts", "*Policy.ts"]
|
||||||
|
primary_signals: ["strategy interface", "context has strategy reference", "interchangeable algorithms"]
|
||||||
|
anti_patterns: ["strategy explosion", "client awareness of strategies"]
|
||||||
|
related: [state, bridge, template-method]
|
||||||
|
stack_native:
|
||||||
|
react: "Custom hooks"
|
||||||
|
vue: "Composables"
|
||||||
|
|
||||||
|
template-method:
|
||||||
|
difficulty: 2
|
||||||
|
use_frequency: medium
|
||||||
|
detection_confidence: 0.75
|
||||||
|
typical_files: ["*Template.ts", "*Abstract*.ts"]
|
||||||
|
primary_signals: ["abstract class", "template method", "primitive operations", "hook methods"]
|
||||||
|
anti_patterns: ["too many hooks", "subclass override complexity"]
|
||||||
|
related: [strategy, factory-method]
|
||||||
|
|
||||||
|
visitor:
|
||||||
|
difficulty: 4
|
||||||
|
use_frequency: low
|
||||||
|
detection_confidence: 0.60
|
||||||
|
typical_files: ["*Visitor.ts"]
|
||||||
|
primary_signals: ["visit* methods", "double dispatch", "element accepts visitor", "operations on object structure"]
|
||||||
|
anti_patterns: ["adding new element types", "circular dependencies"]
|
||||||
|
related: [composite, iterator]
|
||||||
|
|
||||||
|
interpreter:
|
||||||
|
difficulty: 4
|
||||||
|
use_frequency: very-low
|
||||||
|
detection_confidence: 0.55
|
||||||
|
typical_files: ["*Interpreter.ts", "*Expression.ts"]
|
||||||
|
primary_signals: ["grammar rules", "interpret() method", "abstract syntax tree", "terminal/non-terminal expressions"]
|
||||||
|
anti_patterns: ["complex grammars", "performance issues"]
|
||||||
|
related: [composite, visitor]
|
||||||
|
common_use_cases: ["DSLs", "query languages", "expression evaluators"]
|
||||||
|
|
||||||
|
# Detection difficulty levels
|
||||||
|
difficulty_levels:
|
||||||
|
1: "Easy - Clear structural markers and naming conventions"
|
||||||
|
2: "Medium - Requires pattern knowledge to distinguish from similar patterns"
|
||||||
|
3: "Hard - Subtle structural differences, context-dependent"
|
||||||
|
4: "Very Hard - Requires deep analysis of intent and relationships"
|
||||||
|
|
||||||
|
# Usage frequency in modern codebases
|
||||||
|
frequency_levels:
|
||||||
|
very-high: "Ubiquitous in modern frameworks"
|
||||||
|
high: "Common in most projects"
|
||||||
|
medium: "Occasional use, specific scenarios"
|
||||||
|
low: "Rare, specialized use cases"
|
||||||
|
very-low: "Academic or niche applications"
|
||||||
|
|
||||||
|
# Confidence score interpretation
|
||||||
|
confidence_interpretation:
|
||||||
|
0.85-1.0: "High confidence - Multiple strong signals, unlikely false positive"
|
||||||
|
0.70-0.84: "Medium-high - Clear signals, some ambiguity possible"
|
||||||
|
0.55-0.69: "Medium - Requires manual validation"
|
||||||
|
0.40-0.54: "Low - Weak signals, high false positive risk"
|
||||||
|
0.0-0.39: "Very low - Naming convention only, not reliable"
|
||||||
873
examples/skills/design-patterns/reference/structural.md
Normal file
873
examples/skills/design-patterns/reference/structural.md
Normal file
|
|
@ -0,0 +1,873 @@
|
||||||
|
# Structural Design Patterns
|
||||||
|
|
||||||
|
Patterns that deal with object composition and relationships between entities, providing ways to assemble objects and classes into larger structures.
|
||||||
|
|
||||||
|
## Adapter
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Converts the interface of a class into another interface clients expect, allowing incompatible interfaces to work together.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Want to use an existing class with an incompatible interface
|
||||||
|
- [x] Need to integrate third-party libraries with different interfaces
|
||||||
|
- [x] Want to create a reusable class that cooperates with unrelated classes
|
||||||
|
- [x] Legacy code must work with new systems
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Target interface (what client expects)
|
||||||
|
interface Target {
|
||||||
|
request(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adaptee (existing incompatible class)
|
||||||
|
class Adaptee {
|
||||||
|
specificRequest(): string {
|
||||||
|
return '.eetpadA eht fo roivaheb laicepS';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapter (makes Adaptee compatible with Target)
|
||||||
|
class Adapter implements Target {
|
||||||
|
private adaptee: Adaptee;
|
||||||
|
|
||||||
|
constructor(adaptee: Adaptee) {
|
||||||
|
this.adaptee = adaptee;
|
||||||
|
}
|
||||||
|
|
||||||
|
public request(): string {
|
||||||
|
const result = this.adaptee.specificRequest().split('').reverse().join('');
|
||||||
|
return `Adapter: ${result}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client code
|
||||||
|
function clientCode(target: Target) {
|
||||||
|
console.log(target.request());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const adaptee = new Adaptee();
|
||||||
|
const adapter = new Adapter(adaptee);
|
||||||
|
clientCode(adapter);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Real-World Example: Third-Party Library Integration
|
||||||
|
```typescript
|
||||||
|
// Third-party library (can't modify)
|
||||||
|
class XMLDataProvider {
|
||||||
|
getXMLData(): string {
|
||||||
|
return '<data><item>1</item></data>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Your application expects JSON
|
||||||
|
interface JSONDataProvider {
|
||||||
|
getJSONData(): object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapter
|
||||||
|
class XMLToJSONAdapter implements JSONDataProvider {
|
||||||
|
constructor(private xmlProvider: XMLDataProvider) {}
|
||||||
|
|
||||||
|
getJSONData(): object {
|
||||||
|
const xml = this.xmlProvider.getXMLData();
|
||||||
|
// Convert XML to JSON (simplified)
|
||||||
|
return { data: { item: '1' } };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const xmlProvider = new XMLDataProvider();
|
||||||
|
const adapter = new XMLToJSONAdapter(xmlProvider);
|
||||||
|
const data = adapter.getJSONData();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Class implements target interface
|
||||||
|
- Holds reference to adaptee
|
||||||
|
- Delegates to adaptee with interface conversion
|
||||||
|
- Names like `*Adapter`, `*Wrapper`
|
||||||
|
|
||||||
|
### Code Smells It Fixes
|
||||||
|
- **Incompatible interfaces**: Makes legacy or third-party code compatible
|
||||||
|
- **Interface proliferation**: Single adapter vs modifying multiple client calls
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Two-way adapters**: Bidirectional conversion is complex; create two adapters
|
||||||
|
- **Adapter chains**: Multiple adapters in sequence indicate design issues
|
||||||
|
- **Overusing for new code**: Design compatible interfaces from the start
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bridge
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Decouples an abstraction from its implementation so the two can vary independently.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Want to avoid permanent binding between abstraction and implementation
|
||||||
|
- [x] Both abstractions and implementations should be extensible by subclassing
|
||||||
|
- [x] Changes in implementation shouldn't affect clients
|
||||||
|
- [x] Want to share implementation among multiple objects (Flyweight-like)
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Implementation interface
|
||||||
|
interface Implementation {
|
||||||
|
operationImpl(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete implementations
|
||||||
|
class ConcreteImplementationA implements Implementation {
|
||||||
|
operationImpl(): string {
|
||||||
|
return 'ConcreteImplementationA';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConcreteImplementationB implements Implementation {
|
||||||
|
operationImpl(): string {
|
||||||
|
return 'ConcreteImplementationB';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstraction
|
||||||
|
class Abstraction {
|
||||||
|
constructor(protected implementation: Implementation) {}
|
||||||
|
|
||||||
|
public operation(): string {
|
||||||
|
return `Abstraction: ${this.implementation.operationImpl()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refined abstraction
|
||||||
|
class ExtendedAbstraction extends Abstraction {
|
||||||
|
public operation(): string {
|
||||||
|
return `ExtendedAbstraction: ${this.implementation.operationImpl()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const implA = new ConcreteImplementationA();
|
||||||
|
const abstraction1 = new Abstraction(implA);
|
||||||
|
console.log(abstraction1.operation());
|
||||||
|
|
||||||
|
const implB = new ConcreteImplementationB();
|
||||||
|
const abstraction2 = new ExtendedAbstraction(implB);
|
||||||
|
console.log(abstraction2.operation());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Real-World Example: UI Components with Multiple Renderers
|
||||||
|
```typescript
|
||||||
|
// Implementation: Renderers
|
||||||
|
interface Renderer {
|
||||||
|
renderCircle(radius: number): string;
|
||||||
|
renderSquare(side: number): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class VectorRenderer implements Renderer {
|
||||||
|
renderCircle(radius: number): string {
|
||||||
|
return `Drawing circle (vector) with radius ${radius}`;
|
||||||
|
}
|
||||||
|
renderSquare(side: number): string {
|
||||||
|
return `Drawing square (vector) with side ${side}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RasterRenderer implements Renderer {
|
||||||
|
renderCircle(radius: number): string {
|
||||||
|
return `Drawing circle (pixels) with radius ${radius}`;
|
||||||
|
}
|
||||||
|
renderSquare(side: number): string {
|
||||||
|
return `Drawing square (pixels) with side ${side}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstraction: Shapes
|
||||||
|
abstract class Shape {
|
||||||
|
constructor(protected renderer: Renderer) {}
|
||||||
|
abstract draw(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Circle extends Shape {
|
||||||
|
constructor(renderer: Renderer, private radius: number) {
|
||||||
|
super(renderer);
|
||||||
|
}
|
||||||
|
draw(): string {
|
||||||
|
return this.renderer.renderCircle(this.radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Square extends Shape {
|
||||||
|
constructor(renderer: Renderer, private side: number) {
|
||||||
|
super(renderer);
|
||||||
|
}
|
||||||
|
draw(): string {
|
||||||
|
return this.renderer.renderSquare(this.side);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage: Can mix any shape with any renderer
|
||||||
|
const vectorCircle = new Circle(new VectorRenderer(), 5);
|
||||||
|
const rasterSquare = new Square(new RasterRenderer(), 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Abstraction holds reference to implementation interface
|
||||||
|
- Constructor injects implementation
|
||||||
|
- Two parallel hierarchies (abstraction and implementation)
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Confusion with Adapter**: Bridge is design-time; Adapter is runtime fix
|
||||||
|
- **Over-engineering simple scenarios**: Use only when both hierarchies need to vary
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Composite
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Composes objects into tree structures to represent part-whole hierarchies, letting clients treat individual objects and compositions uniformly.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Want to represent part-whole hierarchies of objects
|
||||||
|
- [x] Want clients to ignore difference between compositions and individual objects
|
||||||
|
- [x] Tree structures are natural for the domain (file systems, UI components, org charts)
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Component interface
|
||||||
|
interface Component {
|
||||||
|
operation(): string;
|
||||||
|
add?(component: Component): void;
|
||||||
|
remove?(component: Component): void;
|
||||||
|
getChild?(index: number): Component;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leaf (no children)
|
||||||
|
class Leaf implements Component {
|
||||||
|
constructor(private name: string) {}
|
||||||
|
|
||||||
|
operation(): string {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Composite (has children)
|
||||||
|
class Composite implements Component {
|
||||||
|
private children: Component[] = [];
|
||||||
|
|
||||||
|
constructor(private name: string) {}
|
||||||
|
|
||||||
|
add(component: Component): void {
|
||||||
|
this.children.push(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(component: Component): void {
|
||||||
|
const index = this.children.indexOf(component);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.children.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getChild(index: number): Component {
|
||||||
|
return this.children[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
operation(): string {
|
||||||
|
const results = this.children.map(child => child.operation());
|
||||||
|
return `${this.name}(${results.join(', ')})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const tree = new Composite('root');
|
||||||
|
const branch1 = new Composite('branch1');
|
||||||
|
branch1.add(new Leaf('leaf1'));
|
||||||
|
branch1.add(new Leaf('leaf2'));
|
||||||
|
|
||||||
|
const branch2 = new Composite('branch2');
|
||||||
|
branch2.add(new Leaf('leaf3'));
|
||||||
|
|
||||||
|
tree.add(branch1);
|
||||||
|
tree.add(branch2);
|
||||||
|
tree.add(new Leaf('leaf4'));
|
||||||
|
|
||||||
|
console.log(tree.operation());
|
||||||
|
// Output: root(branch1(leaf1, leaf2), branch2(leaf3), leaf4)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Real-World Example: File System
|
||||||
|
```typescript
|
||||||
|
interface FileSystemComponent {
|
||||||
|
getName(): string;
|
||||||
|
getSize(): number;
|
||||||
|
print(indent: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class File implements FileSystemComponent {
|
||||||
|
constructor(private name: string, private size: number) {}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize(): number {
|
||||||
|
return this.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(indent: string): void {
|
||||||
|
console.log(`${indent}📄 ${this.name} (${this.size} bytes)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Directory implements FileSystemComponent {
|
||||||
|
private children: FileSystemComponent[] = [];
|
||||||
|
|
||||||
|
constructor(private name: string) {}
|
||||||
|
|
||||||
|
add(component: FileSystemComponent): void {
|
||||||
|
this.children.push(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize(): number {
|
||||||
|
return this.children.reduce((sum, child) => sum + child.getSize(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
print(indent: string): void {
|
||||||
|
console.log(`${indent}📁 ${this.name} (${this.getSize()} bytes)`);
|
||||||
|
this.children.forEach(child => child.print(indent + ' '));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const root = new Directory('root');
|
||||||
|
const home = new Directory('home');
|
||||||
|
home.add(new File('photo.jpg', 2048));
|
||||||
|
home.add(new File('document.pdf', 4096));
|
||||||
|
|
||||||
|
const work = new Directory('work');
|
||||||
|
work.add(new File('report.docx', 8192));
|
||||||
|
|
||||||
|
root.add(home);
|
||||||
|
root.add(work);
|
||||||
|
root.print('');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Tree structure with uniform interface
|
||||||
|
- Collection of children components
|
||||||
|
- `add()`, `remove()`, `getChild()` methods
|
||||||
|
- Recursive operation calls
|
||||||
|
|
||||||
|
### Code Smells It Fixes
|
||||||
|
- **Type checking for composition vs leaf**: Uniform interface eliminates `instanceof` checks
|
||||||
|
- **Different handling for parts vs wholes**: Clients treat both uniformly
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Violating uniformity**: Leaf and Composite should have same interface
|
||||||
|
- **Incorrect child management**: Not handling removal properly
|
||||||
|
- **Deep recursion**: Can cause stack overflow on very deep trees
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Decorator
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing for extending functionality.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Need to add responsibilities to objects dynamically and transparently
|
||||||
|
- [x] Responsibilities can be withdrawn
|
||||||
|
- [x] Extension by subclassing is impractical (many possible combinations)
|
||||||
|
- [x] Want to add features incrementally
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Component interface
|
||||||
|
interface Component {
|
||||||
|
operation(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete component
|
||||||
|
class ConcreteComponent implements Component {
|
||||||
|
operation(): string {
|
||||||
|
return 'ConcreteComponent';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base decorator
|
||||||
|
abstract class Decorator implements Component {
|
||||||
|
constructor(protected component: Component) {}
|
||||||
|
|
||||||
|
operation(): string {
|
||||||
|
return this.component.operation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concrete decorators
|
||||||
|
class DecoratorA extends Decorator {
|
||||||
|
operation(): string {
|
||||||
|
return `DecoratorA(${super.operation()})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DecoratorB extends Decorator {
|
||||||
|
operation(): string {
|
||||||
|
return `DecoratorB(${super.operation()})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage: Stack decorators
|
||||||
|
const simple = new ConcreteComponent();
|
||||||
|
const decorated1 = new DecoratorA(simple);
|
||||||
|
const decorated2 = new DecoratorB(decorated1);
|
||||||
|
console.log(decorated2.operation());
|
||||||
|
// Output: DecoratorB(DecoratorA(ConcreteComponent))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stack-Native Alternatives
|
||||||
|
|
||||||
|
**React - Higher-Order Components**:
|
||||||
|
```typescript
|
||||||
|
// HOC decorator
|
||||||
|
function withAuth<P extends object>(
|
||||||
|
Component: React.ComponentType<P>
|
||||||
|
): React.ComponentType<P> {
|
||||||
|
return (props: P) => {
|
||||||
|
const { user } = useAuth();
|
||||||
|
if (!user) return <Redirect to="/login" />;
|
||||||
|
return <Component {...props} />;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage: Stack decorators
|
||||||
|
const AuthenticatedProfile = withAuth(Profile);
|
||||||
|
const AuthenticatedAdminProfile = withLogging(withAuth(Profile));
|
||||||
|
```
|
||||||
|
|
||||||
|
**NestJS - Interceptors**:
|
||||||
|
```typescript
|
||||||
|
@Injectable()
|
||||||
|
export class LoggingInterceptor implements NestInterceptor {
|
||||||
|
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||||
|
console.log('Before...');
|
||||||
|
return next.handle().pipe(
|
||||||
|
tap(() => console.log('After...'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply decorator
|
||||||
|
@UseInterceptors(LoggingInterceptor)
|
||||||
|
@Controller('users')
|
||||||
|
export class UsersController {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Implements same interface as wrapped object
|
||||||
|
- Holds reference to wrapped object
|
||||||
|
- Delegates to wrapped, adding behavior
|
||||||
|
- Can be stacked
|
||||||
|
|
||||||
|
### Code Smells It Fixes
|
||||||
|
- **Class explosion**: Avoid creating subclass for every feature combination
|
||||||
|
- **Rigid feature addition**: Add/remove features dynamically
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Order dependency**: DecoratorA(DecoratorB(x)) ≠ DecoratorB(DecoratorA(x))
|
||||||
|
- **Decorator explosion**: Too many small decorators can be hard to manage
|
||||||
|
- **Breaking interface**: Decorator must maintain interface contract
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Facade
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Provides a unified interface to a set of interfaces in a subsystem, making the subsystem easier to use.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Want to provide a simple interface to a complex subsystem
|
||||||
|
- [x] Many dependencies exist between clients and implementation classes
|
||||||
|
- [x] Want to layer subsystems
|
||||||
|
- [x] Need to decouple subsystem from clients
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Complex subsystem classes
|
||||||
|
class SubsystemA {
|
||||||
|
operationA(): string {
|
||||||
|
return 'SubsystemA';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubsystemB {
|
||||||
|
operationB(): string {
|
||||||
|
return 'SubsystemB';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubsystemC {
|
||||||
|
operationC(): string {
|
||||||
|
return 'SubsystemC';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Facade
|
||||||
|
class Facade {
|
||||||
|
private subsystemA: SubsystemA;
|
||||||
|
private subsystemB: SubsystemB;
|
||||||
|
private subsystemC: SubsystemC;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.subsystemA = new SubsystemA();
|
||||||
|
this.subsystemB = new SubsystemB();
|
||||||
|
this.subsystemC = new SubsystemC();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simplified interface
|
||||||
|
public simpleOperation(): string {
|
||||||
|
const resultA = this.subsystemA.operationA();
|
||||||
|
const resultB = this.subsystemB.operationB();
|
||||||
|
const resultC = this.subsystemC.operationC();
|
||||||
|
return `Facade coordinates: ${resultA}, ${resultB}, ${resultC}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client code
|
||||||
|
const facade = new Facade();
|
||||||
|
console.log(facade.simpleOperation());
|
||||||
|
// Instead of:
|
||||||
|
// const a = new SubsystemA(); const b = new SubsystemB(); const c = new SubsystemC();
|
||||||
|
// a.operationA(); b.operationB(); c.operationC();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Real-World Example: Payment Processing
|
||||||
|
```typescript
|
||||||
|
// Complex subsystems
|
||||||
|
class PaymentValidator {
|
||||||
|
validate(amount: number, card: string): boolean {
|
||||||
|
// Complex validation logic
|
||||||
|
return amount > 0 && card.length === 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentGateway {
|
||||||
|
charge(amount: number, card: string): string {
|
||||||
|
return `Charged $${amount} to ${card}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotificationService {
|
||||||
|
sendReceipt(email: string, transactionId: string): void {
|
||||||
|
console.log(`Receipt sent to ${email}: ${transactionId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TransactionLogger {
|
||||||
|
log(transaction: string): void {
|
||||||
|
console.log(`Logged: ${transaction}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Facade
|
||||||
|
class PaymentFacade {
|
||||||
|
private validator = new PaymentValidator();
|
||||||
|
private gateway = new PaymentGateway();
|
||||||
|
private notifications = new NotificationService();
|
||||||
|
private logger = new TransactionLogger();
|
||||||
|
|
||||||
|
processPayment(amount: number, card: string, email: string): boolean {
|
||||||
|
// Simplified interface for complex process
|
||||||
|
if (!this.validator.validate(amount, card)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = this.gateway.charge(amount, card);
|
||||||
|
this.logger.log(result);
|
||||||
|
this.notifications.sendReceipt(email, result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client code (simple!)
|
||||||
|
const payment = new PaymentFacade();
|
||||||
|
payment.processPayment(100, '1234567890123456', 'user@example.com');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Class with multiple subsystem dependencies
|
||||||
|
- Simple public methods coordinating subsystems
|
||||||
|
- Named `*Facade`, `*API`, `*Service`
|
||||||
|
|
||||||
|
### Code Smells It Fixes
|
||||||
|
- **Complex subsystem usage**: Clients don't need to know subsystem details
|
||||||
|
- **Tight coupling**: Clients depend on facade, not many classes
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **God Facade**: Facade does too much; should coordinate, not contain logic
|
||||||
|
- **Leaky abstraction**: Exposing subsystem details defeats the purpose
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Flyweight
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Uses sharing to support large numbers of fine-grained objects efficiently by storing shared state externally.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Application uses large number of objects
|
||||||
|
- [x] Storage cost is high due to object quantity
|
||||||
|
- [x] Most object state can be made extrinsic (externalized)
|
||||||
|
- [x] Many groups of objects may be replaced by relatively few shared objects
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Flyweight
|
||||||
|
class Flyweight {
|
||||||
|
constructor(private sharedState: string) {}
|
||||||
|
|
||||||
|
operation(uniqueState: string): void {
|
||||||
|
console.log(`Flyweight: Shared (${this.sharedState}) and unique (${uniqueState}) state.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flyweight factory
|
||||||
|
class FlyweightFactory {
|
||||||
|
private flyweights: Map<string, Flyweight> = new Map();
|
||||||
|
|
||||||
|
constructor(initialFlyweights: string[][]) {
|
||||||
|
for (const state of initialFlyweights) {
|
||||||
|
this.flyweights.set(this.getKey(state), new Flyweight(state.join('_')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getKey(state: string[]): string {
|
||||||
|
return state.join('_');
|
||||||
|
}
|
||||||
|
|
||||||
|
getFlyweight(sharedState: string[]): Flyweight {
|
||||||
|
const key = this.getKey(sharedState);
|
||||||
|
|
||||||
|
if (!this.flyweights.has(key)) {
|
||||||
|
console.log('Creating new flyweight');
|
||||||
|
this.flyweights.set(key, new Flyweight(key));
|
||||||
|
} else {
|
||||||
|
console.log('Reusing existing flyweight');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.flyweights.get(key)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
listFlyweights(): void {
|
||||||
|
console.log(`FlyweightFactory: ${this.flyweights.size} flyweights:`);
|
||||||
|
for (const key of this.flyweights.keys()) {
|
||||||
|
console.log(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const factory = new FlyweightFactory([
|
||||||
|
['Chevrolet', 'Camaro2018', 'pink'],
|
||||||
|
['Mercedes Benz', 'C300', 'black'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const flyweight1 = factory.getFlyweight(['Chevrolet', 'Camaro2018', 'pink']);
|
||||||
|
flyweight1.operation('license-123');
|
||||||
|
|
||||||
|
const flyweight2 = factory.getFlyweight(['Chevrolet', 'Camaro2018', 'pink']);
|
||||||
|
flyweight2.operation('license-456'); // Reuses same flyweight
|
||||||
|
```
|
||||||
|
|
||||||
|
### Real-World Example: Text Editor Characters
|
||||||
|
```typescript
|
||||||
|
// Flyweight: Character formatting (shared)
|
||||||
|
class CharacterFormat {
|
||||||
|
constructor(
|
||||||
|
public font: string,
|
||||||
|
public size: number,
|
||||||
|
public color: string
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flyweight factory
|
||||||
|
class FormatFactory {
|
||||||
|
private formats = new Map<string, CharacterFormat>();
|
||||||
|
|
||||||
|
getFormat(font: string, size: number, color: string): CharacterFormat {
|
||||||
|
const key = `${font}_${size}_${color}`;
|
||||||
|
if (!this.formats.has(key)) {
|
||||||
|
this.formats.set(key, new CharacterFormat(font, size, color));
|
||||||
|
}
|
||||||
|
return this.formats.get(key)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Character with extrinsic state
|
||||||
|
class Character {
|
||||||
|
constructor(
|
||||||
|
private char: string,
|
||||||
|
private format: CharacterFormat // Shared flyweight
|
||||||
|
) {}
|
||||||
|
|
||||||
|
render(position: number): string {
|
||||||
|
return `'${this.char}' at ${position} (${this.format.font}, ${this.format.size}px, ${this.format.color})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Document
|
||||||
|
const formatFactory = new FormatFactory();
|
||||||
|
const arial12Black = formatFactory.getFormat('Arial', 12, 'black');
|
||||||
|
const arial12Red = formatFactory.getFormat('Arial', 12, 'red');
|
||||||
|
|
||||||
|
// 10,000 characters, but only 2 format objects
|
||||||
|
const characters: Character[] = [];
|
||||||
|
for (let i = 0; i < 10000; i++) {
|
||||||
|
const format = i % 2 === 0 ? arial12Black : arial12Red;
|
||||||
|
characters.push(new Character('A', format));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Factory managing pool of shared objects
|
||||||
|
- Intrinsic (shared) vs extrinsic (unique) state separation
|
||||||
|
- Map/cache of flyweights
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Premature optimization**: Only use if memory is actually a problem
|
||||||
|
- **Incorrect state separation**: Mixing intrinsic and extrinsic state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Proxy
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
Provides a surrogate or placeholder for another object to control access to it.
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
- [x] Lazy initialization (virtual proxy): Create expensive object only when needed
|
||||||
|
- [x] Access control (protection proxy): Control access to original object
|
||||||
|
- [x] Local representative of remote object (remote proxy)
|
||||||
|
- [x] Logging, caching, or monitoring access
|
||||||
|
|
||||||
|
### TypeScript Signature
|
||||||
|
```typescript
|
||||||
|
// Subject interface
|
||||||
|
interface Subject {
|
||||||
|
request(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real subject
|
||||||
|
class RealSubject implements Subject {
|
||||||
|
request(): void {
|
||||||
|
console.log('RealSubject: Handling request');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy
|
||||||
|
class Proxy implements Subject {
|
||||||
|
private realSubject: RealSubject | null = null;
|
||||||
|
|
||||||
|
request(): void {
|
||||||
|
// Access control
|
||||||
|
if (this.checkAccess()) {
|
||||||
|
// Lazy initialization
|
||||||
|
if (!this.realSubject) {
|
||||||
|
this.realSubject = new RealSubject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
this.logAccess();
|
||||||
|
|
||||||
|
// Delegate to real subject
|
||||||
|
this.realSubject.request();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkAccess(): boolean {
|
||||||
|
console.log('Proxy: Checking access');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private logAccess(): void {
|
||||||
|
console.log('Proxy: Logging access time');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const proxy = new Proxy();
|
||||||
|
proxy.request();
|
||||||
|
// Output:
|
||||||
|
// Proxy: Checking access
|
||||||
|
// Proxy: Logging access time
|
||||||
|
// RealSubject: Handling request
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modern JavaScript Proxy
|
||||||
|
```typescript
|
||||||
|
const target = {
|
||||||
|
message: 'Hello',
|
||||||
|
getValue() {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handler = {
|
||||||
|
get(target: any, prop: string) {
|
||||||
|
console.log(`Accessing property: ${prop}`);
|
||||||
|
return target[prop];
|
||||||
|
},
|
||||||
|
set(target: any, prop: string, value: any) {
|
||||||
|
console.log(`Setting property: ${prop} = ${value}`);
|
||||||
|
target[prop] = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const proxy = new Proxy(target, handler);
|
||||||
|
console.log(proxy.message); // Logs: Accessing property: message
|
||||||
|
proxy.message = 'World'; // Logs: Setting property: message = World
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detection Markers
|
||||||
|
- Implements same interface as real subject
|
||||||
|
- Holds reference to real subject
|
||||||
|
- Controls access (checks, logging, caching)
|
||||||
|
- Lazy initialization of real subject
|
||||||
|
|
||||||
|
### Common Mistakes
|
||||||
|
- **Proxy chains**: Multiple proxies wrapping each other
|
||||||
|
- **Performance overhead**: Every access goes through proxy
|
||||||
|
- **Confusion with Decorator**: Proxy controls access; Decorator adds behavior
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary Table
|
||||||
|
|
||||||
|
| Pattern | Complexity | Use Frequency | Main Benefit |
|
||||||
|
|---------|------------|---------------|--------------|
|
||||||
|
| Adapter | Low | High | Interface compatibility |
|
||||||
|
| Bridge | High | Low | Decouple abstraction from implementation |
|
||||||
|
| Composite | Medium | High | Uniform tree structure handling |
|
||||||
|
| Decorator | Medium | High | Dynamic responsibility addition |
|
||||||
|
| Facade | Low | Very High | Simplified subsystem interface |
|
||||||
|
| Flyweight | High | Low | Memory optimization |
|
||||||
|
| Proxy | Medium | Medium | Controlled access |
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Adapter vs Bridge**: Adapter fixes incompatibility; Bridge designs flexibility
|
||||||
|
2. **Decorator vs Proxy**: Decorator adds features; Proxy controls access
|
||||||
|
3. **Facade simplicity**: Should coordinate, not contain business logic
|
||||||
|
4. **Composite uniformity**: Leaf and Composite must share interface
|
||||||
|
5. **Use native Proxy**: JavaScript `Proxy` object for dynamic property access
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- *Design Patterns: Elements of Reusable Object-Oriented Software* (Gang of Four)
|
||||||
|
- [MDN: Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
|
||||||
|
- [Refactoring Guru: Structural Patterns](https://refactoring.guru/design-patterns/structural-patterns)
|
||||||
478
examples/skills/design-patterns/signatures/code-smells.yaml
Normal file
478
examples/skills/design-patterns/signatures/code-smells.yaml
Normal file
|
|
@ -0,0 +1,478 @@
|
||||||
|
version: "1.0.0"
|
||||||
|
description: Mapping from code smells to applicable design patterns with detection rules and rationale
|
||||||
|
|
||||||
|
# Code Smell Categories
|
||||||
|
categories:
|
||||||
|
- bloaters # Code that has grown too large
|
||||||
|
- oo_abusers # Misuse of object-oriented principles
|
||||||
|
- change_preventers # Changes require modifications in many places
|
||||||
|
- dispensables # Pointless code that should be removed
|
||||||
|
- couplers # Excessive coupling between classes
|
||||||
|
|
||||||
|
# Code Smells with Pattern Suggestions
|
||||||
|
code_smells:
|
||||||
|
switch_on_type:
|
||||||
|
category: oo_abusers
|
||||||
|
description: "Switch statement (or if-else chain) discriminating on object type or kind"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "switch\\s*\\([^)]*\\.type\\)"
|
||||||
|
- "switch\\s*\\([^)]*\\.kind\\)"
|
||||||
|
- "if\\s*\\([^)]*instanceof\\s+\\w+\\).*else\\s+if\\s*\\([^)]*instanceof"
|
||||||
|
context: "Function or method with >3 cases"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: strategy
|
||||||
|
priority: high
|
||||||
|
rationale: "Replace conditional logic with strategy objects"
|
||||||
|
example: |
|
||||||
|
// Before (smell)
|
||||||
|
switch (paymentType) {
|
||||||
|
case 'credit': return processCreditCard();
|
||||||
|
case 'paypal': return processPaypal();
|
||||||
|
case 'crypto': return processCrypto();
|
||||||
|
}
|
||||||
|
|
||||||
|
// After (Strategy)
|
||||||
|
const strategy = strategies[paymentType];
|
||||||
|
return strategy.process();
|
||||||
|
|
||||||
|
- pattern: factory-method
|
||||||
|
priority: medium
|
||||||
|
rationale: "If creating objects based on type"
|
||||||
|
example: |
|
||||||
|
// Before (smell)
|
||||||
|
switch (type) {
|
||||||
|
case 'A': return new ProductA();
|
||||||
|
case 'B': return new ProductB();
|
||||||
|
}
|
||||||
|
|
||||||
|
// After (Factory)
|
||||||
|
return ProductFactory.create(type);
|
||||||
|
|
||||||
|
long_parameter_list:
|
||||||
|
category: bloaters
|
||||||
|
description: "Function/constructor with more than 4 parameters"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "function\\s+\\w+\\s*\\([^)]{100,}\\)" # Heuristic: >100 chars
|
||||||
|
- "constructor\\s*\\([^)]{80,}\\)"
|
||||||
|
context: "Count commas in parameter list"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: builder
|
||||||
|
priority: high
|
||||||
|
rationale: "Fluent interface for step-by-step construction"
|
||||||
|
example: |
|
||||||
|
// Before (smell)
|
||||||
|
new House(walls, doors, windows, roof, garage, pool, garden);
|
||||||
|
|
||||||
|
// After (Builder)
|
||||||
|
new HouseBuilder()
|
||||||
|
.setWalls(walls)
|
||||||
|
.setDoors(doors)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
- pattern: parameter-object
|
||||||
|
priority: medium
|
||||||
|
rationale: "Group related parameters into object"
|
||||||
|
example: |
|
||||||
|
// Before (smell)
|
||||||
|
function createUser(name, email, age, address, phone, role) { }
|
||||||
|
|
||||||
|
// After (Parameter Object)
|
||||||
|
function createUser(userData: UserData) { }
|
||||||
|
|
||||||
|
global_state_access:
|
||||||
|
category: couplers
|
||||||
|
description: "Scattered access to global variables or singletons"
|
||||||
|
severity: high
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "window\\.[A-Z]\\w+"
|
||||||
|
- "global\\.[A-Z]\\w+"
|
||||||
|
- "\\w+\\.getInstance\\(\\)"
|
||||||
|
context: "Outside of configuration/initialization files"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: dependency-injection
|
||||||
|
priority: high
|
||||||
|
rationale: "Inject dependencies instead of global access"
|
||||||
|
example: |
|
||||||
|
// Before (smell)
|
||||||
|
const config = Config.getInstance();
|
||||||
|
|
||||||
|
// After (DI)
|
||||||
|
class Service {
|
||||||
|
constructor(private config: Config) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
- pattern: singleton
|
||||||
|
priority: low
|
||||||
|
rationale: "If truly global state is needed (use sparingly)"
|
||||||
|
note: "Consider framework-native alternatives (React Context, Angular Services)"
|
||||||
|
|
||||||
|
duplicated_conditionals_on_state:
|
||||||
|
category: change_preventers
|
||||||
|
description: "Same state checks scattered across methods"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "if\\s*\\([^)]*\\.state\\s*===\\s*['\"]\\w+['\"]\\).*if\\s*\\([^)]*\\.state\\s*===\\s*['\"]\\w+['\"]\\)"
|
||||||
|
- "switch\\s*\\([^)]*\\.status\\)"
|
||||||
|
context: "Multiple methods with identical state checks"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: state
|
||||||
|
priority: high
|
||||||
|
rationale: "Encapsulate state-dependent behavior in state classes"
|
||||||
|
example: |
|
||||||
|
// Before (smell)
|
||||||
|
if (order.status === 'draft') { /* ... */ }
|
||||||
|
else if (order.status === 'submitted') { /* ... */ }
|
||||||
|
|
||||||
|
// After (State pattern)
|
||||||
|
order.state.process(); // Behavior varies by state object
|
||||||
|
|
||||||
|
scattered_notification_logic:
|
||||||
|
category: change_preventers
|
||||||
|
description: "Manual notification loops in multiple places"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "listeners\\.forEach\\(\\s*\\w+\\s*=>\\s*\\w+\\.\\w+\\(\\)"
|
||||||
|
- "for\\s*\\([^)]*observers[^)]*\\).*\\.update\\("
|
||||||
|
context: "Multiple locations with similar notification code"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: observer
|
||||||
|
priority: high
|
||||||
|
rationale: "Centralize subscription and notification mechanism"
|
||||||
|
example: |
|
||||||
|
// Before (smell)
|
||||||
|
for (const listener of this.listeners) {
|
||||||
|
listener.notify(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// After (Observer)
|
||||||
|
this.subject.notify(data); // Handles all observers
|
||||||
|
|
||||||
|
- pattern: event-emitter
|
||||||
|
priority: medium
|
||||||
|
rationale: "Use built-in EventEmitter (Node.js) or EventTarget"
|
||||||
|
stack_native:
|
||||||
|
javascript: "EventTarget, EventEmitter"
|
||||||
|
rxjs: "Subject, BehaviorSubject"
|
||||||
|
|
||||||
|
complex_object_creation:
|
||||||
|
category: bloaters
|
||||||
|
description: "Object construction requires many steps or conditional logic"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "new\\s+\\w+\\([^)]{80,}\\)" # Long constructor call
|
||||||
|
- "const\\s+\\w+\\s*=\\s*new\\s+\\w+\\(\\);[^}]{50,}\\w+\\.set\\w+"
|
||||||
|
context: "Construction spans multiple lines with setters"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: builder
|
||||||
|
priority: high
|
||||||
|
rationale: "Step-by-step construction with fluent interface"
|
||||||
|
|
||||||
|
- pattern: factory-method
|
||||||
|
priority: medium
|
||||||
|
rationale: "Encapsulate creation logic"
|
||||||
|
|
||||||
|
- pattern: abstract-factory
|
||||||
|
priority: low
|
||||||
|
rationale: "If creating families of related objects"
|
||||||
|
|
||||||
|
tight_coupling_to_concrete_classes:
|
||||||
|
category: couplers
|
||||||
|
description: "Direct instantiation of concrete classes throughout codebase"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "new\\s+Concrete\\w+\\("
|
||||||
|
- "new\\s+\\w+Impl\\("
|
||||||
|
context: "Multiple files instantiating same concrete classes"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: factory-method
|
||||||
|
priority: high
|
||||||
|
rationale: "Depend on interfaces, not concrete classes"
|
||||||
|
|
||||||
|
- pattern: dependency-injection
|
||||||
|
priority: high
|
||||||
|
rationale: "Inject dependencies instead of creating them"
|
||||||
|
|
||||||
|
- pattern: adapter
|
||||||
|
priority: medium
|
||||||
|
rationale: "If integrating incompatible third-party code"
|
||||||
|
|
||||||
|
repetitive_interface_conversions:
|
||||||
|
category: dispensables
|
||||||
|
description: "Manual conversion between incompatible interfaces"
|
||||||
|
severity: low
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "\\{\\s*\\w+:\\s*\\w+\\.\\w+,.*\\w+:\\s*\\w+\\.\\w+" # Object mapping
|
||||||
|
context: "Same mapping logic repeated in multiple places"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: adapter
|
||||||
|
priority: high
|
||||||
|
rationale: "Single adapter class for interface conversion"
|
||||||
|
|
||||||
|
deep_nesting_for_feature_addition:
|
||||||
|
category: change_preventers
|
||||||
|
description: "Adding features requires modifying deeply nested code"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "class\\s+\\w+\\s+extends\\s+\\w+\\s+extends\\s+\\w+" # Multi-level inheritance
|
||||||
|
context: "Inheritance hierarchy >3 levels deep"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: decorator
|
||||||
|
priority: high
|
||||||
|
rationale: "Compose behavior dynamically instead of deep inheritance"
|
||||||
|
|
||||||
|
- pattern: strategy
|
||||||
|
priority: medium
|
||||||
|
rationale: "If behavior variants are swapped at runtime"
|
||||||
|
|
||||||
|
large_class_many_responsibilities:
|
||||||
|
category: bloaters
|
||||||
|
description: "Class has too many responsibilities (God Object)"
|
||||||
|
severity: high
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "class\\s+\\w+\\s*\\{"
|
||||||
|
context: "Class >300 lines or >20 methods"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: facade
|
||||||
|
priority: high
|
||||||
|
rationale: "If class coordinates multiple subsystems"
|
||||||
|
|
||||||
|
- pattern: strategy
|
||||||
|
priority: medium
|
||||||
|
rationale: "Extract varying behavior into strategies"
|
||||||
|
|
||||||
|
- pattern: extract-class
|
||||||
|
priority: high
|
||||||
|
rationale: "Split into multiple focused classes"
|
||||||
|
|
||||||
|
complex_conditional_logic:
|
||||||
|
category: bloaters
|
||||||
|
description: "Deeply nested conditionals or complex boolean expressions"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "if\\s*\\([^)]*&&[^)]*&&[^)]*\\)" # Multiple conditions
|
||||||
|
- "if\\s*\\(.*if\\s*\\(.*if\\s*\\(" # Nested ifs
|
||||||
|
context: "Cyclomatic complexity >10"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: strategy
|
||||||
|
priority: high
|
||||||
|
rationale: "Replace conditional with strategy object"
|
||||||
|
|
||||||
|
- pattern: state
|
||||||
|
priority: medium
|
||||||
|
rationale: "If conditionals check object state"
|
||||||
|
|
||||||
|
- pattern: chain-of-responsibility
|
||||||
|
priority: low
|
||||||
|
rationale: "If processing steps can be chained"
|
||||||
|
|
||||||
|
data_clumps:
|
||||||
|
category: bloaters
|
||||||
|
description: "Same group of parameters appears together frequently"
|
||||||
|
severity: low
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "function\\s+\\w+\\([^)]*userId[^)]*userName[^)]*userEmail[^)]*\\)"
|
||||||
|
context: "Same 3+ parameters in multiple functions"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: parameter-object
|
||||||
|
priority: high
|
||||||
|
rationale: "Group related parameters into object"
|
||||||
|
|
||||||
|
- pattern: builder
|
||||||
|
priority: medium
|
||||||
|
rationale: "If object construction is complex"
|
||||||
|
|
||||||
|
manual_resource_management:
|
||||||
|
category: change_preventers
|
||||||
|
description: "Manual open/close, connect/disconnect, etc."
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "open\\([^)]*\\);.*close\\("
|
||||||
|
- "connect\\([^)]*\\);.*disconnect\\("
|
||||||
|
context: "Scattered resource management logic"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: template-method
|
||||||
|
priority: high
|
||||||
|
rationale: "Template ensures proper initialization/cleanup"
|
||||||
|
|
||||||
|
- pattern: proxy
|
||||||
|
priority: medium
|
||||||
|
rationale: "Proxy manages resource lifecycle"
|
||||||
|
|
||||||
|
feature_envy:
|
||||||
|
category: couplers
|
||||||
|
description: "Method uses more features of another class than its own"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
patterns:
|
||||||
|
- "this\\.(\\w+\\.){3,}" # Multiple chained accesses
|
||||||
|
context: "Method heavily accesses another object's data"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: extract-method
|
||||||
|
priority: high
|
||||||
|
rationale: "Move method to the class it envies"
|
||||||
|
|
||||||
|
- pattern: visitor
|
||||||
|
priority: low
|
||||||
|
rationale: "If operation needs to be external"
|
||||||
|
|
||||||
|
shotgun_surgery:
|
||||||
|
category: change_preventers
|
||||||
|
description: "Single change requires modifying many classes"
|
||||||
|
severity: high
|
||||||
|
|
||||||
|
detection:
|
||||||
|
manual: true
|
||||||
|
context: "Feature addition requires >5 file changes"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: facade
|
||||||
|
priority: high
|
||||||
|
rationale: "Centralize interface to subsystem"
|
||||||
|
|
||||||
|
- pattern: mediator
|
||||||
|
priority: medium
|
||||||
|
rationale: "Centralize communication between objects"
|
||||||
|
|
||||||
|
- pattern: observer
|
||||||
|
priority: medium
|
||||||
|
rationale: "Decouple event sources from handlers"
|
||||||
|
|
||||||
|
parallel_inheritance_hierarchies:
|
||||||
|
category: change_preventers
|
||||||
|
description: "Adding subclass to one hierarchy requires adding to another"
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
detection:
|
||||||
|
manual: true
|
||||||
|
context: "Two inheritance trees grow together"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: bridge
|
||||||
|
priority: high
|
||||||
|
rationale: "Separate abstraction from implementation"
|
||||||
|
|
||||||
|
refused_bequest:
|
||||||
|
category: oo_abusers
|
||||||
|
description: "Subclass doesn't use inherited methods"
|
||||||
|
severity: low
|
||||||
|
|
||||||
|
detection:
|
||||||
|
manual: true
|
||||||
|
context: "Subclass throws errors or leaves methods empty"
|
||||||
|
|
||||||
|
suggested_patterns:
|
||||||
|
- pattern: composition-over-inheritance
|
||||||
|
priority: high
|
||||||
|
rationale: "Use composition instead of inheritance"
|
||||||
|
|
||||||
|
- pattern: strategy
|
||||||
|
priority: medium
|
||||||
|
rationale: "Delegate to strategy object"
|
||||||
|
|
||||||
|
# Detection Priority Rules
|
||||||
|
detection_rules:
|
||||||
|
priority_high:
|
||||||
|
- "Affects >5 files"
|
||||||
|
- "Security implications"
|
||||||
|
- "Performance bottleneck"
|
||||||
|
- "Blocks new features"
|
||||||
|
|
||||||
|
priority_medium:
|
||||||
|
- "Affects 2-5 files"
|
||||||
|
- "Moderate complexity increase"
|
||||||
|
- "Makes testing harder"
|
||||||
|
|
||||||
|
priority_low:
|
||||||
|
- "Affects 1 file"
|
||||||
|
- "Minor code quality issue"
|
||||||
|
- "Cosmetic improvement"
|
||||||
|
|
||||||
|
# Refactoring Impact Assessment
|
||||||
|
impact_assessment:
|
||||||
|
effort:
|
||||||
|
low: "< 2 hours, single file"
|
||||||
|
medium: "2-8 hours, multiple files"
|
||||||
|
high: "> 8 hours, architectural change"
|
||||||
|
|
||||||
|
risk:
|
||||||
|
low: "Localized change, comprehensive tests exist"
|
||||||
|
medium: "Multiple components affected, partial test coverage"
|
||||||
|
high: "Core functionality, limited tests"
|
||||||
|
|
||||||
|
benefit:
|
||||||
|
low: "Minor code quality improvement"
|
||||||
|
medium: "Improved maintainability, easier testing"
|
||||||
|
high: "Significant complexity reduction, enables new features"
|
||||||
|
|
||||||
|
# Stack-Specific Smell Detection
|
||||||
|
stack_specific_smells:
|
||||||
|
react:
|
||||||
|
- smell: "Class components with complex lifecycle methods"
|
||||||
|
pattern: hooks
|
||||||
|
note: "Migrate to functional components with hooks"
|
||||||
|
|
||||||
|
- smell: "Props drilling >3 levels"
|
||||||
|
pattern: context
|
||||||
|
note: "Use Context API for shared state"
|
||||||
|
|
||||||
|
angular:
|
||||||
|
- smell: "Manual subscription management"
|
||||||
|
pattern: async-pipe
|
||||||
|
note: "Use async pipe in templates"
|
||||||
|
|
||||||
|
- smell: "Services with getInstance()"
|
||||||
|
pattern: dependency-injection
|
||||||
|
note: "Use Angular DI with @Injectable()"
|
||||||
|
|
||||||
|
nestjs:
|
||||||
|
- smell: "Manual middleware chaining"
|
||||||
|
pattern: guards-interceptors
|
||||||
|
note: "Use NestJS Guards/Interceptors"
|
||||||
|
|
||||||
|
express:
|
||||||
|
- smell: "Nested callback chains"
|
||||||
|
pattern: async-await
|
||||||
|
note: "Use async/await or Promises"
|
||||||
769
examples/skills/design-patterns/signatures/detection-rules.yaml
Normal file
769
examples/skills/design-patterns/signatures/detection-rules.yaml
Normal file
|
|
@ -0,0 +1,769 @@
|
||||||
|
version: "1.0.0"
|
||||||
|
description: Detection rules and heuristics for identifying design patterns in TypeScript/JavaScript code
|
||||||
|
|
||||||
|
# Detection Strategy:
|
||||||
|
# 1. Glob Phase: Find candidate files by naming convention
|
||||||
|
# 2. Grep Phase: Search for primary and secondary signals
|
||||||
|
# 3. Read Phase: Validate structure and relationships
|
||||||
|
# 4. Confidence Scoring: Aggregate signal strengths
|
||||||
|
|
||||||
|
# Creational Patterns
|
||||||
|
creational:
|
||||||
|
singleton:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Singleton*.ts"
|
||||||
|
- "*Manager.ts"
|
||||||
|
- "*Service.ts"
|
||||||
|
- "*Client.ts"
|
||||||
|
- "*Config.ts"
|
||||||
|
- "*Instance.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "private\\s+constructor"
|
||||||
|
weight: 0.4
|
||||||
|
description: "Private constructor prevents direct instantiation"
|
||||||
|
|
||||||
|
- pattern: "static\\s+getInstance\\s*\\("
|
||||||
|
weight: 0.35
|
||||||
|
description: "Static getInstance method is canonical singleton signature"
|
||||||
|
|
||||||
|
- pattern: "private\\s+static\\s+\\w+:\\s*\\w+"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Private static instance field"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "if\\s*\\(!\\w+\\.instance\\)"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Lazy initialization check"
|
||||||
|
|
||||||
|
- pattern: "return\\s+\\w+\\.instance"
|
||||||
|
weight: 0.10
|
||||||
|
description: "Returns singleton instance"
|
||||||
|
|
||||||
|
typescript_specific:
|
||||||
|
- pattern: "private\\s+static\\s+instance\\?:\\s*\\w+"
|
||||||
|
description: "TypeScript optional instance field"
|
||||||
|
|
||||||
|
- pattern: "public\\s+static\\s+getInstance\\(\\):\\s*\\w+"
|
||||||
|
description: "Explicit return type annotation"
|
||||||
|
|
||||||
|
anti_patterns:
|
||||||
|
- pattern: "new\\s+\\w+\\(\\).*getInstance"
|
||||||
|
description: "Creating new instance inside getInstance (wrong)"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
factory-method:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Factory.ts"
|
||||||
|
- "*Creator.ts"
|
||||||
|
- "*Provider.ts"
|
||||||
|
- "*Builder.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "create\\w+\\s*\\([^)]*\\):\\s*\\w+"
|
||||||
|
weight: 0.35
|
||||||
|
description: "create* method returning interface/type"
|
||||||
|
|
||||||
|
- pattern: "abstract\\s+create\\w+"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Abstract factory method"
|
||||||
|
|
||||||
|
- pattern: "interface\\s+\\w+Factory"
|
||||||
|
weight: 0.20
|
||||||
|
description: "Factory interface definition"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "class\\s+\\w+\\s+extends\\s+\\w+Factory"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Concrete factory inheriting from abstract"
|
||||||
|
|
||||||
|
- pattern: "return\\s+new\\s+\\w+\\("
|
||||||
|
weight: 0.10
|
||||||
|
description: "Factory returns new instance"
|
||||||
|
|
||||||
|
- pattern: "switch\\s*\\([^)]*type[^)]*\\)"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Type-based creation logic"
|
||||||
|
|
||||||
|
typescript_specific:
|
||||||
|
- pattern: "create\\w+<T[^>]*>"
|
||||||
|
description: "Generic factory method"
|
||||||
|
|
||||||
|
confidence_threshold: 0.65
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
abstract-factory:
|
||||||
|
naming_conventions:
|
||||||
|
- "*AbstractFactory.ts"
|
||||||
|
- "*Factory.ts"
|
||||||
|
- "*FactoryInterface.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "abstract\\s+class\\s+\\w+Factory"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Abstract factory class"
|
||||||
|
|
||||||
|
- pattern: "interface\\s+\\w+Factory\\s*\\{[^}]*create\\w+[^}]*create\\w+"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Factory interface with multiple create methods"
|
||||||
|
|
||||||
|
- pattern: "create\\w+\\s*\\([^)]*\\):\\s*\\w+.*create\\w+\\s*\\([^)]*\\):\\s*\\w+"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Multiple factory methods"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "class\\s+\\w+\\s+implements\\s+\\w+Factory"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Concrete factory implementing interface"
|
||||||
|
|
||||||
|
- pattern: "family|related|product"
|
||||||
|
weight: 0.10
|
||||||
|
description: "Domain language suggesting related objects"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
builder:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Builder.ts"
|
||||||
|
- "*ConfigBuilder.ts"
|
||||||
|
- "*QueryBuilder.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "\\w+\\s*\\([^)]*\\):\\s*(this|\\w+Builder)"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Fluent interface returning this or builder type"
|
||||||
|
|
||||||
|
- pattern: "build\\s*\\(\\):\\s*\\w+"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Build method returning final product"
|
||||||
|
|
||||||
|
- pattern: "with\\w+\\s*\\([^)]*\\):\\s*this"
|
||||||
|
weight: 0.25
|
||||||
|
description: "with* methods for setting properties"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "set\\w+\\s*\\([^)]*\\):\\s*this"
|
||||||
|
weight: 0.15
|
||||||
|
description: "set* methods returning this"
|
||||||
|
|
||||||
|
- pattern: "private\\s+\\w+\\?:\\s*\\w+"
|
||||||
|
weight: 0.10
|
||||||
|
description: "Optional private fields being built up"
|
||||||
|
|
||||||
|
typescript_specific:
|
||||||
|
- pattern: "class\\s+\\w+Builder<T"
|
||||||
|
description: "Generic builder"
|
||||||
|
|
||||||
|
- pattern: "\\): this \\{"
|
||||||
|
description: "Explicit 'this' return type"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
prototype:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Prototype.ts"
|
||||||
|
- "*Cloneable.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "clone\\s*\\(\\):\\s*\\w+"
|
||||||
|
weight: 0.45
|
||||||
|
description: "Clone method signature"
|
||||||
|
|
||||||
|
- pattern: "Object\\.create\\("
|
||||||
|
weight: 0.25
|
||||||
|
description: "Using Object.create for prototypal inheritance"
|
||||||
|
|
||||||
|
- pattern: "JSON\\.parse\\(JSON\\.stringify\\("
|
||||||
|
weight: 0.20
|
||||||
|
description: "Deep clone via JSON (simple approach)"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "\\.\\.\\."
|
||||||
|
weight: 0.10
|
||||||
|
description: "Spread operator for shallow copy"
|
||||||
|
|
||||||
|
- pattern: "structuredClone\\("
|
||||||
|
weight: 0.15
|
||||||
|
description: "Modern structuredClone API"
|
||||||
|
|
||||||
|
confidence_threshold: 0.60
|
||||||
|
minimum_signals: 1
|
||||||
|
|
||||||
|
# Structural Patterns
|
||||||
|
structural:
|
||||||
|
adapter:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Adapter.ts"
|
||||||
|
- "*Wrapper.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "class\\s+\\w+Adapter\\s+implements\\s+\\w+"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Adapter implementing target interface"
|
||||||
|
|
||||||
|
- pattern: "private\\s+\\w+:\\s*\\w+;"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Holds reference to adaptee"
|
||||||
|
|
||||||
|
- pattern: "constructor\\([^)]*adaptee[^)]*\\)"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Adaptee injected in constructor"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "this\\.\\w+\\.\\w+\\("
|
||||||
|
weight: 0.15
|
||||||
|
description: "Delegates to adaptee"
|
||||||
|
|
||||||
|
- pattern: "convert|transform|map"
|
||||||
|
weight: 0.10
|
||||||
|
description: "Conversion language in methods"
|
||||||
|
|
||||||
|
confidence_threshold: 0.65
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
bridge:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Bridge.ts"
|
||||||
|
- "*Abstraction.ts"
|
||||||
|
- "*Implementation.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "protected\\s+impl:\\s*\\w+Implementation"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Abstraction holds implementation reference"
|
||||||
|
|
||||||
|
- pattern: "interface\\s+\\w+Implementation"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Implementation interface"
|
||||||
|
|
||||||
|
- pattern: "constructor\\([^)]*implementation[^)]*\\)"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Implementation injected"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "this\\.impl\\."
|
||||||
|
weight: 0.15
|
||||||
|
description: "Delegates to implementation"
|
||||||
|
|
||||||
|
confidence_threshold: 0.60
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
composite:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Composite.ts"
|
||||||
|
- "*Component.ts"
|
||||||
|
- "*Container.ts"
|
||||||
|
- "*Group.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "private\\s+children:\\s*\\w+\\[\\]"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Collection of child components"
|
||||||
|
|
||||||
|
- pattern: "add\\s*\\([^)]*\\):|remove\\s*\\([^)]*\\):"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Methods to manage children"
|
||||||
|
|
||||||
|
- pattern: "abstract\\s+class\\s+\\w+Component"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Abstract component class"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "children\\.forEach\\(|children\\.map\\("
|
||||||
|
weight: 0.15
|
||||||
|
description: "Iterates over children"
|
||||||
|
|
||||||
|
- pattern: "getChild\\(|getChildren\\("
|
||||||
|
weight: 0.10
|
||||||
|
description: "Access to children"
|
||||||
|
|
||||||
|
typescript_specific:
|
||||||
|
- pattern: "children:\\s*Array<\\w+>"
|
||||||
|
description: "Typed array of components"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
decorator:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Decorator.ts"
|
||||||
|
- "*Wrapper.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "class\\s+\\w+Decorator\\s+implements\\s+\\w+"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Decorator implements same interface"
|
||||||
|
|
||||||
|
- pattern: "protected\\s+wrapped:\\s*\\w+"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Holds reference to wrapped object"
|
||||||
|
|
||||||
|
- pattern: "constructor\\([^)]*wrapped[^)]*\\)"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Wrapped object injected"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "this\\.wrapped\\.\\w+\\("
|
||||||
|
weight: 0.20
|
||||||
|
description: "Delegates to wrapped object"
|
||||||
|
|
||||||
|
- pattern: "super\\(wrapped\\)"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Passes wrapped to base decorator"
|
||||||
|
|
||||||
|
typescript_specific:
|
||||||
|
- pattern: "@\\w+"
|
||||||
|
description: "TypeScript decorator syntax (different usage)"
|
||||||
|
|
||||||
|
confidence_threshold: 0.65
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
facade:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Facade.ts"
|
||||||
|
- "*API.ts"
|
||||||
|
- "*Interface.ts"
|
||||||
|
- "*Service.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "private\\s+\\w+Service:\\s*\\w+;[^}]*private\\s+\\w+Service:\\s*\\w+"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Multiple subsystem dependencies"
|
||||||
|
|
||||||
|
- pattern: "constructor\\([^)]*,\\s*[^)]*,\\s*[^)]*\\)"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Multiple injected dependencies"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "this\\.\\w+Service\\.\\w+\\([^)]*\\);[^}]*this\\.\\w+Service\\.\\w+\\("
|
||||||
|
weight: 0.20
|
||||||
|
description: "Coordinates multiple subsystems"
|
||||||
|
|
||||||
|
- pattern: "public\\s+async\\s+\\w+\\([^)]*\\)"
|
||||||
|
weight: 0.10
|
||||||
|
description: "Simplified public interface"
|
||||||
|
|
||||||
|
confidence_threshold: 0.60
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
flyweight:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Flyweight.ts"
|
||||||
|
- "*Pool.ts"
|
||||||
|
- "*Cache.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "private\\s+static\\s+pool:\\s*Map"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Shared pool of flyweights"
|
||||||
|
|
||||||
|
- pattern: "getFlyweight\\s*\\([^)]*\\):\\s*\\w+"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Factory method for shared objects"
|
||||||
|
|
||||||
|
- pattern: "if\\s*\\(!pool\\.has\\([^)]*\\)\\)"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Check if flyweight exists before creating"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "intrinsic|extrinsic"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Domain language for shared/unique state"
|
||||||
|
|
||||||
|
confidence_threshold: 0.65
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
proxy:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Proxy.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "class\\s+\\w+Proxy\\s+implements\\s+\\w+"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Proxy implements same interface as real subject"
|
||||||
|
|
||||||
|
- pattern: "private\\s+realSubject:\\s*\\w+"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Holds reference to real subject"
|
||||||
|
|
||||||
|
- pattern: "lazy|cache|access|log"
|
||||||
|
weight: 0.20
|
||||||
|
description: "Proxy purpose keywords"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "if\\s*\\(!this\\.realSubject\\)"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Lazy initialization"
|
||||||
|
|
||||||
|
- pattern: "this\\.realSubject\\.\\w+\\("
|
||||||
|
weight: 0.15
|
||||||
|
description: "Delegates to real subject"
|
||||||
|
|
||||||
|
typescript_specific:
|
||||||
|
- pattern: "new\\s+Proxy\\("
|
||||||
|
description: "JavaScript Proxy object"
|
||||||
|
|
||||||
|
confidence_threshold: 0.65
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
# Behavioral Patterns
|
||||||
|
behavioral:
|
||||||
|
chain-of-responsibility:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Handler.ts"
|
||||||
|
- "*Middleware.ts"
|
||||||
|
- "*Chain.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "protected\\s+next:\\s*\\w+Handler"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Reference to next handler"
|
||||||
|
|
||||||
|
- pattern: "setNext\\s*\\([^)]*\\):\\s*\\w+Handler"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Method to set next handler"
|
||||||
|
|
||||||
|
- pattern: "handle\\s*\\([^)]*\\):"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Handle method"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "if\\s*\\(this\\.next\\)"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Checks if next handler exists"
|
||||||
|
|
||||||
|
- pattern: "this\\.next\\.handle\\("
|
||||||
|
weight: 0.15
|
||||||
|
description: "Passes request to next"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
command:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Command.ts"
|
||||||
|
- "*Action.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "interface\\s+\\w+Command\\s*\\{[^}]*execute"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Command interface with execute"
|
||||||
|
|
||||||
|
- pattern: "execute\\s*\\(\\):"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Execute method"
|
||||||
|
|
||||||
|
- pattern: "undo\\s*\\(\\):"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Undo method for reversibility"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "private\\s+receiver:\\s*\\w+"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Holds receiver reference"
|
||||||
|
|
||||||
|
- pattern: "history|stack"
|
||||||
|
weight: 0.10
|
||||||
|
description: "Command history tracking"
|
||||||
|
|
||||||
|
confidence_threshold: 0.65
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
iterator:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Iterator.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "\\[Symbol\\.iterator\\]\\(\\)"
|
||||||
|
weight: 0.40
|
||||||
|
description: "JavaScript iterator protocol"
|
||||||
|
|
||||||
|
- pattern: "next\\s*\\(\\):\\s*\\{\\s*value[^}]*done"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Iterator next method"
|
||||||
|
|
||||||
|
- pattern: "function\\*|\\*\\w+\\s*\\("
|
||||||
|
weight: 0.25
|
||||||
|
description: "Generator function"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "yield"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Generator yield"
|
||||||
|
|
||||||
|
- pattern: "implements\\s+Iterator<"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Implements Iterator interface"
|
||||||
|
|
||||||
|
confidence_threshold: 0.65
|
||||||
|
minimum_signals: 1
|
||||||
|
|
||||||
|
mediator:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Mediator.ts"
|
||||||
|
- "*Controller.ts"
|
||||||
|
- "*Coordinator.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "class\\s+\\w+Mediator"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Mediator class"
|
||||||
|
|
||||||
|
- pattern: "private\\s+colleagues:\\s*\\w+\\[\\]"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Collection of colleagues"
|
||||||
|
|
||||||
|
- pattern: "notify\\s*\\([^)]*sender[^)]*\\)"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Notify method with sender"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "register\\w+\\(|add\\w+\\("
|
||||||
|
weight: 0.15
|
||||||
|
description: "Register colleagues"
|
||||||
|
|
||||||
|
confidence_threshold: 0.65
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
memento:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Memento.ts"
|
||||||
|
- "*Snapshot.ts"
|
||||||
|
- "*Caretaker.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "save\\s*\\(\\):\\s*\\w+Memento"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Save state to memento"
|
||||||
|
|
||||||
|
- pattern: "restore\\s*\\([^)]*memento[^)]*\\)"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Restore from memento"
|
||||||
|
|
||||||
|
- pattern: "class\\s+\\w+Memento"
|
||||||
|
weight: 0.20
|
||||||
|
description: "Memento class"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "private\\s+state:"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Encapsulated state"
|
||||||
|
|
||||||
|
- pattern: "history:\\s*\\w+\\[\\]"
|
||||||
|
weight: 0.10
|
||||||
|
description: "History of mementos"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
observer:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Observer.ts"
|
||||||
|
- "*Subject.ts"
|
||||||
|
- "*Publisher.ts"
|
||||||
|
- "*Subscriber.ts"
|
||||||
|
- "*Event*.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "subscribe\\s*\\([^)]*\\):|attach\\s*\\([^)]*\\):"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Subscribe method"
|
||||||
|
|
||||||
|
- pattern: "unsubscribe\\s*\\([^)]*\\):|detach\\s*\\([^)]*\\):"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Unsubscribe method"
|
||||||
|
|
||||||
|
- pattern: "notify\\s*\\(\\):|emit\\s*\\("
|
||||||
|
weight: 0.30
|
||||||
|
description: "Notify/emit method"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "private\\s+observers:\\s*\\w+\\[\\]"
|
||||||
|
weight: 0.20
|
||||||
|
description: "Collection of observers"
|
||||||
|
|
||||||
|
- pattern: "observers\\.forEach\\("
|
||||||
|
weight: 0.15
|
||||||
|
description: "Iterates over observers to notify"
|
||||||
|
|
||||||
|
typescript_specific:
|
||||||
|
- pattern: "Subject<"
|
||||||
|
description: "RxJS Subject (common in Angular/React)"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
state:
|
||||||
|
naming_conventions:
|
||||||
|
- "*State.ts"
|
||||||
|
- "*Context.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "interface\\s+\\w+State"
|
||||||
|
weight: 0.30
|
||||||
|
description: "State interface"
|
||||||
|
|
||||||
|
- pattern: "private\\s+state:\\s*\\w+State"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Context holds current state"
|
||||||
|
|
||||||
|
- pattern: "setState\\s*\\([^)]*\\):|changeState\\s*\\([^)]*\\):"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Method to change state"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "this\\.state\\.handle\\("
|
||||||
|
weight: 0.15
|
||||||
|
description: "Delegates to state"
|
||||||
|
|
||||||
|
- pattern: "class\\s+\\w+State\\s+implements\\s+\\w+State"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Concrete state classes"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Strategy.ts"
|
||||||
|
- "*Policy.ts"
|
||||||
|
- "*Algorithm.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "interface\\s+\\w+Strategy"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Strategy interface"
|
||||||
|
|
||||||
|
- pattern: "private\\s+strategy:\\s*\\w+Strategy"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Context holds strategy"
|
||||||
|
|
||||||
|
- pattern: "setStrategy\\s*\\([^)]*\\):"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Method to set strategy"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "this\\.strategy\\.execute\\("
|
||||||
|
weight: 0.15
|
||||||
|
description: "Delegates to strategy"
|
||||||
|
|
||||||
|
- pattern: "class\\s+\\w+Strategy\\s+implements\\s+\\w+Strategy"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Concrete strategies"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
template-method:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Template.ts"
|
||||||
|
- "*Abstract*.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "abstract\\s+class"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Abstract class with template"
|
||||||
|
|
||||||
|
- pattern: "abstract\\s+\\w+\\s*\\(\\):"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Abstract primitive operations"
|
||||||
|
|
||||||
|
- pattern: "protected\\s+\\w+\\s*\\(\\):"
|
||||||
|
weight: 0.20
|
||||||
|
description: "Hook methods"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "this\\.\\w+\\(\\);[^}]*this\\.\\w+\\(\\);"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Template calls primitive operations"
|
||||||
|
|
||||||
|
- pattern: "final\\s+|\\btemplate\\b"
|
||||||
|
weight: 0.10
|
||||||
|
description: "Template method should be final (not overridden)"
|
||||||
|
|
||||||
|
confidence_threshold: 0.65
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
visitor:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Visitor.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "interface\\s+\\w+Visitor\\s*\\{[^}]*visit\\w+"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Visitor interface with visit methods"
|
||||||
|
|
||||||
|
- pattern: "visit\\w+\\s*\\([^)]*:\\s*\\w+\\)"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Visit methods for each element type"
|
||||||
|
|
||||||
|
- pattern: "accept\\s*\\([^)]*visitor[^)]*\\)"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Elements accept visitor"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "visitor\\.visit\\w+\\(this\\)"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Double dispatch pattern"
|
||||||
|
|
||||||
|
confidence_threshold: 0.70
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
interpreter:
|
||||||
|
naming_conventions:
|
||||||
|
- "*Interpreter.ts"
|
||||||
|
- "*Expression.ts"
|
||||||
|
- "*Parser.ts"
|
||||||
|
|
||||||
|
primary_signals:
|
||||||
|
- pattern: "interface\\s+\\w+Expression"
|
||||||
|
weight: 0.30
|
||||||
|
description: "Expression interface"
|
||||||
|
|
||||||
|
- pattern: "interpret\\s*\\([^)]*context[^)]*\\)"
|
||||||
|
weight: 0.35
|
||||||
|
description: "Interpret method with context"
|
||||||
|
|
||||||
|
- pattern: "abstract\\s+class\\s+\\w+Expression"
|
||||||
|
weight: 0.25
|
||||||
|
description: "Abstract expression"
|
||||||
|
|
||||||
|
secondary_signals:
|
||||||
|
- pattern: "class\\s+Terminal\\w+|class\\s+NonTerminal\\w+"
|
||||||
|
weight: 0.15
|
||||||
|
description: "Terminal and non-terminal expressions"
|
||||||
|
|
||||||
|
- pattern: "parse\\s*\\("
|
||||||
|
weight: 0.10
|
||||||
|
description: "Parser for grammar"
|
||||||
|
|
||||||
|
confidence_threshold: 0.60
|
||||||
|
minimum_signals: 2
|
||||||
|
|
||||||
|
# Confidence Calculation
|
||||||
|
confidence_calculation:
|
||||||
|
formula: "sum(matched_signal_weights) / sum(all_possible_weights)"
|
||||||
|
adjustments:
|
||||||
|
- "If pattern name matches naming convention: +0.15"
|
||||||
|
- "If stack has native equivalent and not used: -0.20"
|
||||||
|
- "If anti-pattern detected: -0.30"
|
||||||
|
- "If typescript-specific signals present: +0.10"
|
||||||
|
|
||||||
|
# Output Format for Detection Results
|
||||||
|
detection_output:
|
||||||
|
file: "path/to/file.ts"
|
||||||
|
pattern: "singleton"
|
||||||
|
lines: "5-28"
|
||||||
|
confidence: 0.85
|
||||||
|
matched_signals:
|
||||||
|
- signal: "private constructor"
|
||||||
|
weight: 0.4
|
||||||
|
- signal: "static getInstance"
|
||||||
|
weight: 0.35
|
||||||
|
stack_context:
|
||||||
|
native_alternative: "Context API"
|
||||||
|
recommendation: "Consider using React Context for better testability"
|
||||||
582
examples/skills/design-patterns/signatures/stack-patterns.yaml
Normal file
582
examples/skills/design-patterns/signatures/stack-patterns.yaml
Normal file
|
|
@ -0,0 +1,582 @@
|
||||||
|
version: "1.0.0"
|
||||||
|
description: Stack detection rules and pattern adaptations for framework-native idioms
|
||||||
|
|
||||||
|
# Stack Detection Rules
|
||||||
|
stack_detection:
|
||||||
|
sources:
|
||||||
|
- file: "package.json"
|
||||||
|
check: "dependencies + devDependencies"
|
||||||
|
priority: 1
|
||||||
|
- file: "tsconfig.json"
|
||||||
|
check: "compilerOptions, paths, lib"
|
||||||
|
priority: 2
|
||||||
|
- files: ["angular.json", "next.config.*", "nest-cli.json", "vite.config.*", "nuxt.config.*"]
|
||||||
|
check: "presence"
|
||||||
|
priority: 1
|
||||||
|
- pattern: "*.jsx, *.tsx, *.vue, *.svelte"
|
||||||
|
check: "file extensions"
|
||||||
|
priority: 3
|
||||||
|
|
||||||
|
confidence_scoring:
|
||||||
|
high: "Primary framework found in package.json + config file present"
|
||||||
|
medium: "Framework in package.json OR config file present"
|
||||||
|
low: "Only file extensions or indirect evidence"
|
||||||
|
|
||||||
|
# Supported Stacks
|
||||||
|
stacks:
|
||||||
|
react:
|
||||||
|
detection:
|
||||||
|
package_json:
|
||||||
|
required: ["react"]
|
||||||
|
optional: ["react-dom", "next", "@types/react"]
|
||||||
|
files:
|
||||||
|
optional: ["next.config.js", "next.config.ts", "vite.config.ts"]
|
||||||
|
extensions: [".jsx", ".tsx"]
|
||||||
|
|
||||||
|
version_detection:
|
||||||
|
hooks_era: ">=16.8.0" # useState, useEffect introduced
|
||||||
|
concurrent: ">=18.0.0" # Concurrent features
|
||||||
|
|
||||||
|
native_patterns:
|
||||||
|
singleton:
|
||||||
|
replacement: "Context API + Provider"
|
||||||
|
example: |
|
||||||
|
// Instead of Singleton
|
||||||
|
const MyContext = createContext<ContextValue>(defaultValue);
|
||||||
|
export const MyProvider = ({ children }) => (
|
||||||
|
<MyContext.Provider value={value}>{children}</MyContext.Provider>
|
||||||
|
);
|
||||||
|
rationale: "Context provides dependency injection without global state"
|
||||||
|
confidence: 0.95
|
||||||
|
|
||||||
|
observer:
|
||||||
|
replacement: "useState + useEffect"
|
||||||
|
example: |
|
||||||
|
const [value, setValue] = useState(initial);
|
||||||
|
useEffect(() => {
|
||||||
|
// React auto-notifies subscribers
|
||||||
|
}, [value]);
|
||||||
|
rationale: "React's reactivity system is built-in observer pattern"
|
||||||
|
confidence: 0.98
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
replacement: "Custom hooks"
|
||||||
|
example: |
|
||||||
|
const usePaymentStrategy = (type: PaymentType) => {
|
||||||
|
const strategies = {
|
||||||
|
credit: useCreditPayment(),
|
||||||
|
paypal: usePaypalPayment(),
|
||||||
|
};
|
||||||
|
return strategies[type];
|
||||||
|
};
|
||||||
|
rationale: "Hooks encapsulate behavior and are composable"
|
||||||
|
confidence: 0.90
|
||||||
|
|
||||||
|
decorator:
|
||||||
|
replacement: "Higher-Order Components (HOC)"
|
||||||
|
example: |
|
||||||
|
const withAuth = <P extends object>(Component: React.ComponentType<P>) => {
|
||||||
|
return (props: P) => {
|
||||||
|
const { user } = useAuth();
|
||||||
|
if (!user) return <Redirect to="/login" />;
|
||||||
|
return <Component {...props} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
rationale: "HOCs wrap components to add behavior"
|
||||||
|
confidence: 0.85
|
||||||
|
|
||||||
|
mediator:
|
||||||
|
replacement: "Context API"
|
||||||
|
example: |
|
||||||
|
// Centralized state management
|
||||||
|
const AppContext = createContext<AppState>(initialState);
|
||||||
|
// Components communicate through context
|
||||||
|
rationale: "Context mediates communication between components"
|
||||||
|
confidence: 0.80
|
||||||
|
|
||||||
|
recommendations:
|
||||||
|
- "Prefer hooks over class-based patterns"
|
||||||
|
- "Use Context for shared state, not Singleton"
|
||||||
|
- "Leverage built-in reactivity instead of manual observer"
|
||||||
|
- "Consider React Query/SWR for data fetching patterns"
|
||||||
|
|
||||||
|
angular:
|
||||||
|
detection:
|
||||||
|
package_json:
|
||||||
|
required: ["@angular/core"]
|
||||||
|
optional: ["@angular/common", "@angular/platform-browser"]
|
||||||
|
files:
|
||||||
|
required: ["angular.json"]
|
||||||
|
extensions: [".ts"]
|
||||||
|
|
||||||
|
version_detection:
|
||||||
|
standalone: ">=14.0.0" # Standalone components
|
||||||
|
signals: ">=16.0.0" # Signals API
|
||||||
|
|
||||||
|
native_patterns:
|
||||||
|
singleton:
|
||||||
|
replacement: "@Injectable() services"
|
||||||
|
example: |
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ConfigService {
|
||||||
|
// Automatically singleton via DI
|
||||||
|
}
|
||||||
|
rationale: "Angular's DI container manages singleton lifecycle"
|
||||||
|
confidence: 0.98
|
||||||
|
|
||||||
|
observer:
|
||||||
|
replacement: "RxJS Observables"
|
||||||
|
example: |
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
private data$ = new BehaviorSubject<Data>(initial);
|
||||||
|
getData() { return this.data$.asObservable(); }
|
||||||
|
rationale: "RxJS is Angular's standard reactive programming library"
|
||||||
|
confidence: 0.98
|
||||||
|
|
||||||
|
decorator:
|
||||||
|
replacement: "Directives and Pipes"
|
||||||
|
example: |
|
||||||
|
@Directive({ selector: '[appHighlight]' })
|
||||||
|
export class HighlightDirective {
|
||||||
|
// Adds behavior to DOM elements
|
||||||
|
}
|
||||||
|
rationale: "Angular decorators are first-class pattern"
|
||||||
|
confidence: 0.95
|
||||||
|
|
||||||
|
facade:
|
||||||
|
replacement: "Service with injected dependencies"
|
||||||
|
example: |
|
||||||
|
@Injectable()
|
||||||
|
export class UserFacade {
|
||||||
|
constructor(
|
||||||
|
private userService: UserService,
|
||||||
|
private authService: AuthService,
|
||||||
|
private profileService: ProfileService
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
rationale: "Services naturally encapsulate subsystems"
|
||||||
|
confidence: 0.90
|
||||||
|
|
||||||
|
recommendations:
|
||||||
|
- "Use DI instead of manual singleton patterns"
|
||||||
|
- "Leverage RxJS for all reactive patterns"
|
||||||
|
- "Use Angular decorators (@Component, @Directive, @Pipe)"
|
||||||
|
- "Services should be injected, not manually instantiated"
|
||||||
|
|
||||||
|
nestjs:
|
||||||
|
detection:
|
||||||
|
package_json:
|
||||||
|
required: ["@nestjs/core"]
|
||||||
|
optional: ["@nestjs/common", "@nestjs/platform-express"]
|
||||||
|
files:
|
||||||
|
required: ["nest-cli.json"]
|
||||||
|
extensions: [".ts"]
|
||||||
|
|
||||||
|
native_patterns:
|
||||||
|
singleton:
|
||||||
|
replacement: "@Injectable() with default scope"
|
||||||
|
example: |
|
||||||
|
@Injectable() // Default scope is SINGLETON
|
||||||
|
export class AppService {}
|
||||||
|
rationale: "NestJS DI uses singleton scope by default"
|
||||||
|
confidence: 0.98
|
||||||
|
|
||||||
|
chain-of-responsibility:
|
||||||
|
replacement: "Guards, Interceptors, Pipes, Middleware"
|
||||||
|
example: |
|
||||||
|
@Injectable()
|
||||||
|
export class AuthGuard implements CanActivate {
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
// Guard chain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rationale: "NestJS request pipeline is built-in chain pattern"
|
||||||
|
confidence: 0.95
|
||||||
|
|
||||||
|
decorator:
|
||||||
|
replacement: "Interceptors"
|
||||||
|
example: |
|
||||||
|
@Injectable()
|
||||||
|
export class LoggingInterceptor implements NestInterceptor {
|
||||||
|
intercept(context: ExecutionContext, next: CallHandler) {
|
||||||
|
// Wraps request handling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rationale: "Interceptors add behavior to route handlers"
|
||||||
|
confidence: 0.95
|
||||||
|
|
||||||
|
facade:
|
||||||
|
replacement: "Modules"
|
||||||
|
example: |
|
||||||
|
@Module({
|
||||||
|
providers: [ServiceA, ServiceB],
|
||||||
|
exports: [FacadeService]
|
||||||
|
})
|
||||||
|
export class FeatureModule {}
|
||||||
|
rationale: "Modules encapsulate and export simplified interfaces"
|
||||||
|
confidence: 0.85
|
||||||
|
|
||||||
|
factory:
|
||||||
|
replacement: "useFactory provider"
|
||||||
|
example: |
|
||||||
|
{
|
||||||
|
provide: 'CONFIG',
|
||||||
|
useFactory: (env: EnvService) => createConfig(env),
|
||||||
|
inject: [EnvService]
|
||||||
|
}
|
||||||
|
rationale: "DI supports factory pattern natively"
|
||||||
|
confidence: 0.90
|
||||||
|
|
||||||
|
recommendations:
|
||||||
|
- "Use built-in DI, never manual singleton"
|
||||||
|
- "Leverage Guards/Interceptors/Pipes for cross-cutting concerns"
|
||||||
|
- "Modules should export facades to subsystems"
|
||||||
|
- "Use dynamic modules for configurable features"
|
||||||
|
|
||||||
|
vue:
|
||||||
|
detection:
|
||||||
|
package_json:
|
||||||
|
required: ["vue"]
|
||||||
|
version: ">=3.0.0"
|
||||||
|
files:
|
||||||
|
optional: ["vite.config.ts", "nuxt.config.ts"]
|
||||||
|
extensions: [".vue"]
|
||||||
|
|
||||||
|
version_detection:
|
||||||
|
composition_api: ">=3.0.0" # Composition API default
|
||||||
|
script_setup: ">=3.2.0" # <script setup> sugar
|
||||||
|
|
||||||
|
native_patterns:
|
||||||
|
singleton:
|
||||||
|
replacement: "Provide/Inject"
|
||||||
|
example: |
|
||||||
|
// Root component
|
||||||
|
provide('config', reactive({ theme: 'dark' }));
|
||||||
|
// Child component
|
||||||
|
const config = inject('config');
|
||||||
|
rationale: "Provide/Inject offers dependency injection"
|
||||||
|
confidence: 0.90
|
||||||
|
|
||||||
|
observer:
|
||||||
|
replacement: "ref() and reactive()"
|
||||||
|
example: |
|
||||||
|
const count = ref(0);
|
||||||
|
watch(count, (newVal) => {
|
||||||
|
// Automatically notified on change
|
||||||
|
});
|
||||||
|
rationale: "Vue 3's reactivity is built-in observer"
|
||||||
|
confidence: 0.98
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
replacement: "Composables"
|
||||||
|
example: |
|
||||||
|
export function usePayment(type: PaymentType) {
|
||||||
|
const strategies = {
|
||||||
|
credit: useCreditPayment,
|
||||||
|
paypal: usePaypalPayment,
|
||||||
|
};
|
||||||
|
return strategies[type]();
|
||||||
|
}
|
||||||
|
rationale: "Composables encapsulate reusable logic"
|
||||||
|
confidence: 0.90
|
||||||
|
|
||||||
|
mediator:
|
||||||
|
replacement: "Pinia stores or global state"
|
||||||
|
example: |
|
||||||
|
export const useAppStore = defineStore('app', {
|
||||||
|
state: () => ({ /* ... */ }),
|
||||||
|
actions: { /* ... */ }
|
||||||
|
});
|
||||||
|
rationale: "Pinia centralizes state management"
|
||||||
|
confidence: 0.85
|
||||||
|
|
||||||
|
recommendations:
|
||||||
|
- "Use Composition API for all new code"
|
||||||
|
- "Leverage ref()/reactive() instead of manual observer"
|
||||||
|
- "Use composables for strategy-like patterns"
|
||||||
|
- "Pinia for global state, provide/inject for DI"
|
||||||
|
|
||||||
|
express:
|
||||||
|
detection:
|
||||||
|
package_json:
|
||||||
|
required: ["express"]
|
||||||
|
extensions: [".js", ".ts"]
|
||||||
|
|
||||||
|
native_patterns:
|
||||||
|
chain-of-responsibility:
|
||||||
|
replacement: "Middleware (app.use)"
|
||||||
|
example: |
|
||||||
|
app.use(authMiddleware);
|
||||||
|
app.use(loggingMiddleware);
|
||||||
|
app.use(errorMiddleware);
|
||||||
|
rationale: "Express middleware is chain of responsibility"
|
||||||
|
confidence: 0.98
|
||||||
|
|
||||||
|
facade:
|
||||||
|
replacement: "Router"
|
||||||
|
example: |
|
||||||
|
const userRouter = express.Router();
|
||||||
|
userRouter.get('/', getUsers);
|
||||||
|
userRouter.post('/', createUser);
|
||||||
|
app.use('/users', userRouter);
|
||||||
|
rationale: "Routers group related endpoints"
|
||||||
|
confidence: 0.85
|
||||||
|
|
||||||
|
template-method:
|
||||||
|
replacement: "Route handlers with middleware"
|
||||||
|
example: |
|
||||||
|
app.get('/users',
|
||||||
|
validateRequest, // Step 1
|
||||||
|
checkAuth, // Step 2
|
||||||
|
loadUser, // Step 3
|
||||||
|
(req, res) => {} // Final step
|
||||||
|
);
|
||||||
|
rationale: "Middleware chain defines template"
|
||||||
|
confidence: 0.75
|
||||||
|
|
||||||
|
recommendations:
|
||||||
|
- "Use middleware for cross-cutting concerns"
|
||||||
|
- "Routers should group related functionality"
|
||||||
|
- "Error handling middleware should be last"
|
||||||
|
- "Avoid manual chain implementations"
|
||||||
|
|
||||||
|
rxjs:
|
||||||
|
detection:
|
||||||
|
package_json:
|
||||||
|
required: ["rxjs"]
|
||||||
|
extensions: [".ts", ".js"]
|
||||||
|
|
||||||
|
native_patterns:
|
||||||
|
observer:
|
||||||
|
replacement: "Subject, BehaviorSubject, ReplaySubject"
|
||||||
|
example: |
|
||||||
|
const subject = new BehaviorSubject<number>(0);
|
||||||
|
subject.subscribe(value => console.log(value));
|
||||||
|
subject.next(1); // Notifies subscribers
|
||||||
|
rationale: "RxJS is the observer pattern library"
|
||||||
|
confidence: 1.0
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
replacement: "RxJS operators"
|
||||||
|
example: |
|
||||||
|
observable.pipe(
|
||||||
|
map(x => x * 2),
|
||||||
|
filter(x => x > 10),
|
||||||
|
debounceTime(300)
|
||||||
|
);
|
||||||
|
rationale: "Operators are composable strategies"
|
||||||
|
confidence: 0.90
|
||||||
|
|
||||||
|
decorator:
|
||||||
|
replacement: "pipe() operator"
|
||||||
|
example: |
|
||||||
|
source$.pipe(
|
||||||
|
tap(x => console.log(x)), // Add logging
|
||||||
|
catchError(handleError) // Add error handling
|
||||||
|
);
|
||||||
|
rationale: "pipe() chains operators that decorate behavior"
|
||||||
|
confidence: 0.85
|
||||||
|
|
||||||
|
chain-of-responsibility:
|
||||||
|
replacement: "mergeMap, concatMap, switchMap"
|
||||||
|
example: |
|
||||||
|
requests$.pipe(
|
||||||
|
mergeMap(req => processRequest(req)),
|
||||||
|
catchError((err, caught) => caught) // Pass to next
|
||||||
|
);
|
||||||
|
rationale: "Operators chain processing steps"
|
||||||
|
confidence: 0.75
|
||||||
|
|
||||||
|
recommendations:
|
||||||
|
- "Use Subject variants, not custom observer classes"
|
||||||
|
- "Compose operators instead of manual logic"
|
||||||
|
- "shareReplay() for caching/memoization"
|
||||||
|
- "Avoid manual subscription management, use async pipe (Angular)"
|
||||||
|
|
||||||
|
redux:
|
||||||
|
detection:
|
||||||
|
package_json:
|
||||||
|
required_any: ["redux", "@reduxjs/toolkit", "zustand", "jotai", "recoil"]
|
||||||
|
extensions: [".ts", ".js", ".tsx", ".jsx"]
|
||||||
|
|
||||||
|
native_patterns:
|
||||||
|
singleton:
|
||||||
|
replacement: "Store"
|
||||||
|
example: |
|
||||||
|
// Redux
|
||||||
|
const store = createStore(rootReducer);
|
||||||
|
// Zustand
|
||||||
|
export const useStore = create((set) => ({ /* ... */ }));
|
||||||
|
rationale: "Store is managed singleton"
|
||||||
|
confidence: 0.98
|
||||||
|
|
||||||
|
command:
|
||||||
|
replacement: "Actions and Action Creators"
|
||||||
|
example: |
|
||||||
|
// Redux action
|
||||||
|
const incrementAction = { type: 'INCREMENT', payload: 1 };
|
||||||
|
// Redux Toolkit
|
||||||
|
const increment = createAction<number>('counter/increment');
|
||||||
|
rationale: "Actions encapsulate state changes"
|
||||||
|
confidence: 0.95
|
||||||
|
|
||||||
|
observer:
|
||||||
|
replacement: "Store subscriptions"
|
||||||
|
example: |
|
||||||
|
// Redux
|
||||||
|
store.subscribe(() => console.log(store.getState()));
|
||||||
|
// Zustand (React)
|
||||||
|
const count = useStore(state => state.count);
|
||||||
|
rationale: "Store notifies subscribers on state change"
|
||||||
|
confidence: 0.95
|
||||||
|
|
||||||
|
state:
|
||||||
|
replacement: "Reducers"
|
||||||
|
example: |
|
||||||
|
const reducer = (state, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'INCREMENT': return { count: state.count + 1 };
|
||||||
|
// Different states handled by action type
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rationale: "Reducers implement state-dependent behavior"
|
||||||
|
confidence: 0.85
|
||||||
|
|
||||||
|
memento:
|
||||||
|
replacement: "Time-travel debugging / Redux DevTools"
|
||||||
|
example: |
|
||||||
|
// Redux DevTools captures state snapshots
|
||||||
|
// Can jump to any previous state
|
||||||
|
rationale: "Store history enables memento pattern"
|
||||||
|
confidence: 0.80
|
||||||
|
|
||||||
|
recommendations:
|
||||||
|
- "Use Redux Toolkit for modern Redux (reduces boilerplate)"
|
||||||
|
- "Consider Zustand for simpler state management"
|
||||||
|
- "Actions should be serializable (for time-travel)"
|
||||||
|
- "Selectors should use reselect for memoization"
|
||||||
|
|
||||||
|
orm:
|
||||||
|
detection:
|
||||||
|
package_json:
|
||||||
|
required_any: ["prisma", "@prisma/client", "typeorm", "@mikro-orm/core", "sequelize"]
|
||||||
|
files:
|
||||||
|
optional: ["prisma/schema.prisma", "ormconfig.json"]
|
||||||
|
extensions: [".ts"]
|
||||||
|
|
||||||
|
native_patterns:
|
||||||
|
repository:
|
||||||
|
replacement: "Prisma Client / TypeORM Repository"
|
||||||
|
example: |
|
||||||
|
// Prisma
|
||||||
|
const users = await prisma.user.findMany();
|
||||||
|
// TypeORM
|
||||||
|
const userRepo = dataSource.getRepository(User);
|
||||||
|
const users = await userRepo.find();
|
||||||
|
rationale: "ORMs provide repository pattern out-of-box"
|
||||||
|
confidence: 0.95
|
||||||
|
|
||||||
|
facade:
|
||||||
|
replacement: "ORM Client"
|
||||||
|
example: |
|
||||||
|
// Prisma facades complex SQL
|
||||||
|
await prisma.user.create({
|
||||||
|
data: { name: 'Alice', posts: { create: [{ title: 'Hello' }] } }
|
||||||
|
});
|
||||||
|
rationale: "ORM simplifies database operations"
|
||||||
|
confidence: 0.90
|
||||||
|
|
||||||
|
builder:
|
||||||
|
replacement: "Query Builder"
|
||||||
|
example: |
|
||||||
|
// Prisma
|
||||||
|
prisma.user.findMany({ where: { age: { gte: 18 } }, orderBy: { name: 'asc' } });
|
||||||
|
// TypeORM Query Builder
|
||||||
|
userRepo.createQueryBuilder('user')
|
||||||
|
.where('user.age >= :age', { age: 18 })
|
||||||
|
.orderBy('user.name', 'ASC')
|
||||||
|
.getMany();
|
||||||
|
rationale: "Fluent interface for complex queries"
|
||||||
|
confidence: 0.90
|
||||||
|
|
||||||
|
unit-of-work:
|
||||||
|
replacement: "Transactions"
|
||||||
|
example: |
|
||||||
|
await prisma.$transaction([
|
||||||
|
prisma.user.create({ data: userData }),
|
||||||
|
prisma.post.create({ data: postData })
|
||||||
|
]);
|
||||||
|
rationale: "Transactions implement unit of work"
|
||||||
|
confidence: 0.85
|
||||||
|
|
||||||
|
lazy-loading:
|
||||||
|
replacement: "Include/relations"
|
||||||
|
example: |
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: 1 },
|
||||||
|
include: { posts: true } // Explicit eager loading
|
||||||
|
});
|
||||||
|
rationale: "ORMs control loading strategy"
|
||||||
|
confidence: 0.80
|
||||||
|
|
||||||
|
recommendations:
|
||||||
|
- "Use ORM repositories, not custom implementations"
|
||||||
|
- "Leverage query builders for complex queries"
|
||||||
|
- "Understand N+1 problem, use includes wisely"
|
||||||
|
- "Transactions for multi-model operations"
|
||||||
|
|
||||||
|
# Adaptation Rules
|
||||||
|
adaptation_rules:
|
||||||
|
- condition: "pattern exists natively in stack"
|
||||||
|
action: "suggest_native"
|
||||||
|
priority: "high"
|
||||||
|
message: "Use stack-native implementation instead of custom pattern"
|
||||||
|
|
||||||
|
- condition: "custom implementation found AND native exists"
|
||||||
|
action: "refactor_to_native"
|
||||||
|
priority: "high"
|
||||||
|
message: "Refactor custom implementation to use stack-native idiom"
|
||||||
|
|
||||||
|
- condition: "pattern missing AND native exists"
|
||||||
|
action: "suggest_native_with_example"
|
||||||
|
priority: "medium"
|
||||||
|
message: "Implement using stack-native features"
|
||||||
|
|
||||||
|
- condition: "pattern missing AND no native equivalent"
|
||||||
|
action: "suggest_custom_implementation"
|
||||||
|
priority: "low"
|
||||||
|
message: "Implement custom TypeScript pattern following best practices"
|
||||||
|
|
||||||
|
- condition: "anti-pattern detected"
|
||||||
|
action: "warn_and_suggest_alternative"
|
||||||
|
priority: "critical"
|
||||||
|
message: "Current implementation has known issues, see alternative"
|
||||||
|
|
||||||
|
# Common Stack Combinations
|
||||||
|
stack_combinations:
|
||||||
|
react_full:
|
||||||
|
primary: "react"
|
||||||
|
common_secondary: ["redux", "react-query", "zustand"]
|
||||||
|
pattern_notes: "React Query/SWR replaces many manual patterns for data fetching"
|
||||||
|
|
||||||
|
angular_full:
|
||||||
|
primary: "angular"
|
||||||
|
common_secondary: ["rxjs", "ngrx"]
|
||||||
|
pattern_notes: "RxJS is integral, NgRx adds Redux-like state management"
|
||||||
|
|
||||||
|
nestjs_full:
|
||||||
|
primary: "nestjs"
|
||||||
|
common_secondary: ["typeorm", "prisma"]
|
||||||
|
pattern_notes: "NestJS provides most patterns, ORM adds repository layer"
|
||||||
|
|
||||||
|
vue_full:
|
||||||
|
primary: "vue"
|
||||||
|
common_secondary: ["pinia", "vueuse"]
|
||||||
|
pattern_notes: "Pinia for state, VueUse provides composable utilities"
|
||||||
|
|
||||||
|
# Priority for suggestions
|
||||||
|
suggestion_priority:
|
||||||
|
critical: "Anti-pattern or security issue"
|
||||||
|
high: "Native stack alternative available, significant improvement"
|
||||||
|
medium: "Pattern missing, would improve code quality"
|
||||||
|
low: "Optional improvement, minimal impact"
|
||||||
|
|
@ -4796,6 +4796,154 @@ it('should calculate order total', () => {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Example 3: Design Patterns Analyzer Skill
|
||||||
|
|
||||||
|
**Purpose**: Detect, analyze, and suggest Gang of Four design patterns in TypeScript/JavaScript codebases with stack-aware recommendations.
|
||||||
|
|
||||||
|
**Location**: `examples/skills/design-patterns/`
|
||||||
|
|
||||||
|
**Key Features**:
|
||||||
|
- Detects 23 GoF design patterns (Creational, Structural, Behavioral)
|
||||||
|
- Stack-aware detection (React, Angular, NestJS, Vue, Express, RxJS, Redux, ORMs)
|
||||||
|
- Code smell detection with pattern suggestions
|
||||||
|
- Quality evaluation (5 criteria: Correctness, Testability, SRP, Open/Closed, Documentation)
|
||||||
|
- Prefers stack-native alternatives (e.g., React Context over Singleton)
|
||||||
|
|
||||||
|
**Structure**:
|
||||||
|
```
|
||||||
|
design-patterns/
|
||||||
|
├── SKILL.md # Main skill instructions
|
||||||
|
├── reference/
|
||||||
|
│ ├── patterns-index.yaml # 23 patterns metadata
|
||||||
|
│ ├── creational.md # 5 creational patterns
|
||||||
|
│ ├── structural.md # 7 structural patterns
|
||||||
|
│ └── behavioral.md # 11 behavioral patterns
|
||||||
|
├── signatures/
|
||||||
|
│ ├── stack-patterns.yaml # Stack detection + native alternatives
|
||||||
|
│ ├── detection-rules.yaml # Grep patterns for detection
|
||||||
|
│ └── code-smells.yaml # Smell → pattern mappings
|
||||||
|
└── checklists/
|
||||||
|
└── pattern-evaluation.md # Quality scoring system
|
||||||
|
```
|
||||||
|
|
||||||
|
**Operating Modes**:
|
||||||
|
|
||||||
|
1. **Detection Mode**: Find existing patterns in codebase
|
||||||
|
```bash
|
||||||
|
# Invoke via skill or direct analysis
|
||||||
|
"Analyze design patterns in src/"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Suggestion Mode**: Identify code smells and suggest patterns
|
||||||
|
```bash
|
||||||
|
"Suggest design patterns to fix code smells in src/services/"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Evaluation Mode**: Score pattern implementation quality
|
||||||
|
```bash
|
||||||
|
"Evaluate the Factory pattern implementation in src/lib/errors/"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Output**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"stack_detected": {
|
||||||
|
"primary": "react",
|
||||||
|
"version": "19.0",
|
||||||
|
"secondary": ["typescript", "next.js", "prisma"],
|
||||||
|
"detection_sources": ["package.json", "tsconfig.json"]
|
||||||
|
},
|
||||||
|
"patterns_found": {
|
||||||
|
"factory-method": [{
|
||||||
|
"file": "src/lib/errors/factory.ts",
|
||||||
|
"lines": "12-45",
|
||||||
|
"confidence": 0.9,
|
||||||
|
"quality_score": 8.2,
|
||||||
|
"notes": "Well-implemented with proper abstraction"
|
||||||
|
}],
|
||||||
|
"singleton": [{
|
||||||
|
"file": "src/config.ts",
|
||||||
|
"confidence": 0.85,
|
||||||
|
"quality_score": 4.0,
|
||||||
|
"recommendation": "Consider React Context instead"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"code_smells": [{
|
||||||
|
"type": "switch_on_type",
|
||||||
|
"file": "src/components/data-handler.tsx",
|
||||||
|
"line": 52,
|
||||||
|
"severity": "medium",
|
||||||
|
"suggested_pattern": "strategy",
|
||||||
|
"rationale": "Replace conditional logic with strategy objects"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Stack-Native Recommendations**:
|
||||||
|
|
||||||
|
| Pattern | React Alternative | Angular Alternative | NestJS Alternative |
|
||||||
|
|---------|-------------------|---------------------|-------------------|
|
||||||
|
| Singleton | Context API + Provider | @Injectable() service | @Injectable() (default) |
|
||||||
|
| Observer | useState + useEffect | RxJS Observables | EventEmitter |
|
||||||
|
| Decorator | Higher-Order Component | @Decorator syntax | @Injectable decorators |
|
||||||
|
| Factory | Custom Hook pattern | Factory service | Provider pattern |
|
||||||
|
|
||||||
|
**Detection Methodology**:
|
||||||
|
|
||||||
|
1. **Stack Detection**: Analyze package.json, tsconfig.json, config files
|
||||||
|
2. **Pattern Search**: Use Glob → Grep → Read pipeline
|
||||||
|
- Glob: Find candidate files (`**/*factory*.ts`, `**/*singleton*.ts`)
|
||||||
|
- Grep: Match detection patterns (regex for key structures)
|
||||||
|
- Read: Verify pattern implementation
|
||||||
|
3. **Quality Evaluation**: Score on 5 criteria (0-10 each)
|
||||||
|
4. **Smell Detection**: Identify anti-patterns and suggest refactoring
|
||||||
|
|
||||||
|
**Quality Evaluation Criteria**:
|
||||||
|
|
||||||
|
| Criterion | Weight | Description |
|
||||||
|
|-----------|--------|-------------|
|
||||||
|
| Correctness | 30% | Follows canonical pattern structure |
|
||||||
|
| Testability | 25% | Easy to mock, no global state |
|
||||||
|
| Single Responsibility | 20% | One clear purpose |
|
||||||
|
| Open/Closed | 15% | Extensible without modification |
|
||||||
|
| Documentation | 10% | Clear intent, usage examples |
|
||||||
|
|
||||||
|
**Example Usage in Agent**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
name: architecture-reviewer
|
||||||
|
description: Review system architecture and design patterns
|
||||||
|
tools: Read, Grep, Glob
|
||||||
|
skills:
|
||||||
|
- design-patterns # Inherits pattern knowledge
|
||||||
|
---
|
||||||
|
|
||||||
|
When reviewing architecture:
|
||||||
|
1. Use design-patterns skill to detect existing patterns
|
||||||
|
2. Evaluate pattern implementation quality
|
||||||
|
3. Suggest improvements based on stack-native alternatives
|
||||||
|
4. Check for code smells requiring pattern refactoring
|
||||||
|
```
|
||||||
|
|
||||||
|
**Integration with Méthode Aristote**:
|
||||||
|
|
||||||
|
This skill is now installed in the Méthode Aristote repository at:
|
||||||
|
```
|
||||||
|
/Users/florianbruniaux/Sites/MethodeAristote/app/.claude/skills/design-patterns/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
1. Direct invocation: "Analyze design patterns in src/"
|
||||||
|
2. Via agent: Create an agent that inherits the design-patterns skill
|
||||||
|
3. Automated review: Use in CI/CD to detect pattern violations
|
||||||
|
|
||||||
|
**Reference**:
|
||||||
|
- Full documentation: `examples/skills/design-patterns/SKILL.md`
|
||||||
|
- Pattern reference: `examples/skills/design-patterns/reference/*.md`
|
||||||
|
- Detection rules: `examples/skills/design-patterns/signatures/*.yaml`
|
||||||
|
|
||||||
## 5.5 Community Skill Repositories
|
## 5.5 Community Skill Repositories
|
||||||
|
|
||||||
### Cybersecurity Skills Repository
|
### Cybersecurity Skills Repository
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue