diff --git a/README.md b/README.md index 5be3d84..17e101c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

Stars - Templates + Templates Quiz

@@ -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. -**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). @@ -196,7 +196,7 @@ claude-code-ultimate-guide/
-Examples Library (65 templates) +Examples Library (66 templates) **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) ⭐ diff --git a/examples/README.md b/examples/README.md index f54b639..50cbff5 100644 --- a/examples/README.md +++ b/examples/README.md @@ -56,6 +56,7 @@ Ready-to-use templates for Claude Code configuration. ### Skills | 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 | | [security-checklist.md](./skills/security-checklist.md) | OWASP Top 10 security checks | | [pdf-generator.md](./skills/pdf-generator.md) | Professional PDF generation (Quarto/Typst) | diff --git a/examples/skills/design-patterns/SKILL.md b/examples/skills/design-patterns/SKILL.md new file mode 100644 index 0000000..bfc8104 --- /dev/null +++ b/examples/skills/design-patterns/SKILL.md @@ -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; +} + +// 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((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 diff --git a/examples/skills/design-patterns/checklists/pattern-evaluation.md b/examples/skills/design-patterns/checklists/pattern-evaluation.md new file mode 100644 index 0000000..1bff22f --- /dev/null +++ b/examples/skills/design-patterns/checklists/pattern-evaluation.md @@ -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; +} + +class PaymentProcessor { + constructor(private strategies: Map) {} + + 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) diff --git a/examples/skills/design-patterns/reference/behavioral.md b/examples/skills/design-patterns/reference/behavioral.md new file mode 100644 index 0000000..a5b5a90 --- /dev/null +++ b/examples/skills/design-patterns/reference/behavioral.md @@ -0,0 +1,1032 @@ +# Behavioral Design Patterns + +Patterns concerned with algorithms and the assignment of responsibilities between objects, focusing on communication patterns. + +## Chain of Responsibility + +### Definition +Passes requests along a chain of handlers, where each handler decides either to process the request or pass it to the next handler. + +### When to Use +- [x] More than one object may handle a request, and handler isn't known a priori +- [x] Want to issue request without specifying receiver explicitly +- [x] Set of handlers can be specified dynamically +- [x] Processing order matters + +### TypeScript Signature +```typescript +interface Handler { + setNext(handler: Handler): Handler; + handle(request: string): string | null; +} + +abstract class AbstractHandler implements Handler { + private nextHandler: Handler | null = null; + + setNext(handler: Handler): Handler { + this.nextHandler = handler; + return handler; // Allows chaining: h1.setNext(h2).setNext(h3) + } + + handle(request: string): string | null { + if (this.nextHandler) { + return this.nextHandler.handle(request); + } + return null; + } +} + +class ConcreteHandlerA extends AbstractHandler { + handle(request: string): string | null { + if (request === 'A') { + return `HandlerA processed ${request}`; + } + return super.handle(request); + } +} + +class ConcreteHandlerB extends AbstractHandler { + handle(request: string): string | null { + if (request === 'B') { + return `HandlerB processed ${request}`; + } + return super.handle(request); + } +} + +// Usage +const handlerA = new ConcreteHandlerA(); +const handlerB = new ConcreteHandlerB(); +handlerA.setNext(handlerB); + +console.log(handlerA.handle('B')); // HandlerB processed B +``` + +### Stack-Native Alternatives + +**Express Middleware**: +```typescript +app.use(authMiddleware); +app.use(loggingMiddleware); +app.use(errorMiddleware); +``` + +**NestJS Guards/Interceptors**: +```typescript +@UseGuards(AuthGuard, RolesGuard) +@UseInterceptors(LoggingInterceptor) +``` + +### Code Smells It Fixes +- **Tight coupling to request handler**: Client doesn't know which handler processes request +- **Complex conditional logic**: Each handler has simple logic + +--- + +## Command + +### Definition +Encapsulates a request as an object, letting you parameterize clients with different requests, queue or log requests, and support undoable operations. + +### When to Use +- [x] Parameterize objects with operations +- [x] Queue, specify, and execute requests at different times +- [x] Support undo/redo operations +- [x] Log changes for system crash recovery + +### TypeScript Signature +```typescript +// Command interface +interface Command { + execute(): void; + undo?(): void; +} + +// Receiver +class Light { + turnOn(): void { + console.log('Light is on'); + } + turnOff(): void { + console.log('Light is off'); + } +} + +// Concrete commands +class TurnOnCommand implements Command { + constructor(private light: Light) {} + + execute(): void { + this.light.turnOn(); + } + + undo(): void { + this.light.turnOff(); + } +} + +class TurnOffCommand implements Command { + constructor(private light: Light) {} + + execute(): void { + this.light.turnOff(); + } + + undo(): void { + this.light.turnOn(); + } +} + +// Invoker +class RemoteControl { + private history: Command[] = []; + + execute(command: Command): void { + command.execute(); + this.history.push(command); + } + + undo(): void { + const command = this.history.pop(); + if (command?.undo) { + command.undo(); + } + } +} + +// Usage +const light = new Light(); +const remote = new RemoteControl(); + +remote.execute(new TurnOnCommand(light)); // Light is on +remote.execute(new TurnOffCommand(light)); // Light is off +remote.undo(); // Light is on +``` + +### Stack-Native: Redux Actions +```typescript +const incrementAction = { type: 'INCREMENT', payload: 1 }; +dispatch(incrementAction); // Command pattern +``` + +--- + +## Iterator + +### Definition +Provides a way to access elements of a collection sequentially without exposing its underlying representation. + +### When to Use +- [x] Need to access collection's contents without exposing internal structure +- [x] Support multiple traversals of collections +- [x] Provide uniform interface for traversing different structures + +### TypeScript Signature +```typescript +// Iterator interface +interface Iterator { + next(): { value: T; done: boolean }; + hasNext(): boolean; +} + +// Iterable collection +interface Iterable { + createIterator(): Iterator; +} + +// Concrete iterator +class ArrayIterator implements Iterator { + private position = 0; + + constructor(private collection: T[]) {} + + next(): { value: T; done: boolean } { + if (this.position < this.collection.length) { + return { value: this.collection[this.position++], done: false }; + } + return { value: null as any, done: true }; + } + + hasNext(): boolean { + return this.position < this.collection.length; + } +} + +// Collection +class NumberCollection implements Iterable { + constructor(private items: number[]) {} + + createIterator(): Iterator { + return new ArrayIterator(this.items); + } +} +``` + +### JavaScript Native Support +```typescript +// Symbol.iterator +const collection = { + items: [1, 2, 3], + [Symbol.iterator]() { + let index = 0; + const items = this.items; + return { + next() { + return index < items.length + ? { value: items[index++], done: false } + : { done: true, value: undefined }; + } + }; + } +}; + +for (const item of collection) { + console.log(item); // 1, 2, 3 +} + +// Generator (simpler) +function* numberGenerator() { + yield 1; + yield 2; + yield 3; +} + +for (const num of numberGenerator()) { + console.log(num); +} +``` + +--- + +## Mediator + +### Definition +Defines an object that encapsulates how a set of objects interact, promoting loose coupling by keeping objects from referring to each other explicitly. + +### When to Use +- [x] Set of objects communicate in complex ways +- [x] Reusing object is difficult because it refers to many others +- [x] Behavior distributed between classes should be customizable without subclassing + +### TypeScript Signature +```typescript +// Mediator interface +interface Mediator { + notify(sender: object, event: string): void; +} + +// Concrete mediator +class ConcreteMediator implements Mediator { + private component1: Component1; + private component2: Component2; + + constructor(c1: Component1, c2: Component2) { + this.component1 = c1; + this.component1.setMediator(this); + this.component2 = c2; + this.component2.setMediator(this); + } + + notify(sender: object, event: string): void { + if (event === 'A') { + console.log('Mediator reacts to A and triggers:'); + this.component2.doC(); + } + if (event === 'D') { + console.log('Mediator reacts to D and triggers:'); + this.component1.doB(); + } + } +} + +// Base component +class BaseComponent { + protected mediator: Mediator | null = null; + + setMediator(mediator: Mediator): void { + this.mediator = mediator; + } +} + +// Concrete components +class Component1 extends BaseComponent { + doA(): void { + console.log('Component 1 does A'); + this.mediator?.notify(this, 'A'); + } + + doB(): void { + console.log('Component 1 does B'); + } +} + +class Component2 extends BaseComponent { + doC(): void { + console.log('Component 2 does C'); + } + + doD(): void { + console.log('Component 2 does D'); + this.mediator?.notify(this, 'D'); + } +} + +// Usage +const c1 = new Component1(); +const c2 = new Component2(); +const mediator = new ConcreteMediator(c1, c2); + +c1.doA(); +// Output: +// Component 1 does A +// Mediator reacts to A and triggers: +// Component 2 does C +``` + +### Stack-Native: React Context +```typescript +const ChatContext = createContext(null!); + +// Mediator as context +function ChatRoom({ children }: Props) { + const sendMessage = (from: string, to: string, msg: string) => { + // Mediator logic + }; + + return ( + + {children} + + ); +} +``` + +### Code Smells It Fixes +- **Complex web of interactions**: Centralized in mediator +- **God object with many responsibilities**: Mediator focuses on coordination only + +--- + +## Memento + +### Definition +Captures and externalizes an object's internal state without violating encapsulation, so the object can be restored to this state later. + +### When to Use +- [x] Need to save/restore object snapshots (undo/redo) +- [x] Direct interface to state would expose implementation +- [x] Want to preserve encapsulation boundaries + +### TypeScript Signature +```typescript +// Memento +class Memento { + constructor(private state: string, private date: Date) {} + + getState(): string { + return this.state; + } + + getDate(): Date { + return this.date; + } +} + +// Originator +class Editor { + private content: string = ''; + + type(text: string): void { + this.content += text; + } + + getContent(): string { + return this.content; + } + + save(): Memento { + return new Memento(this.content, new Date()); + } + + restore(memento: Memento): void { + this.content = memento.getState(); + } +} + +// Caretaker +class History { + private mementos: Memento[] = []; + + push(memento: Memento): void { + this.mementos.push(memento); + } + + pop(): Memento | undefined { + return this.mementos.pop(); + } +} + +// Usage +const editor = new Editor(); +const history = new History(); + +editor.type('Hello '); +history.push(editor.save()); + +editor.type('World'); +history.push(editor.save()); + +editor.type('!!!'); +console.log(editor.getContent()); // Hello World!!! + +editor.restore(history.pop()!); +console.log(editor.getContent()); // Hello World +``` + +### Code Smells It Fixes +- **Exposing internal state for undo**: Memento encapsulates state +- **Complex undo logic**: History manages snapshots + +--- + +## Observer + +### Definition +Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified automatically. + +### When to Use +- [x] Change to one object requires changing others (unknown number) +- [x] Object should notify others without knowing who they are +- [x] Event-driven architectures +- [x] Reactive programming + +### TypeScript Signature +```typescript +// Observer interface +interface Observer { + update(subject: Subject): void; +} + +// Subject +interface Subject { + attach(observer: Observer): void; + detach(observer: Observer): void; + notify(): void; +} + +// Concrete subject +class ConcreteSubject implements Subject { + private observers: Observer[] = []; + private state: number = 0; + + attach(observer: Observer): void { + if (!this.observers.includes(observer)) { + this.observers.push(observer); + } + } + + detach(observer: Observer): void { + const index = this.observers.indexOf(observer); + if (index !== -1) { + this.observers.splice(index, 1); + } + } + + notify(): void { + for (const observer of this.observers) { + observer.update(this); + } + } + + setState(state: number): void { + this.state = state; + this.notify(); + } + + getState(): number { + return this.state; + } +} + +// Concrete observers +class ConcreteObserverA implements Observer { + update(subject: ConcreteSubject): void { + console.log(`ObserverA: State is now ${subject.getState()}`); + } +} + +class ConcreteObserverB implements Observer { + update(subject: ConcreteSubject): void { + console.log(`ObserverB: State is now ${subject.getState()}`); + } +} + +// Usage +const subject = new ConcreteSubject(); +const observerA = new ConcreteObserverA(); +const observerB = new ConcreteObserverB(); + +subject.attach(observerA); +subject.attach(observerB); + +subject.setState(5); +// Output: +// ObserverA: State is now 5 +// ObserverB: State is now 5 +``` + +### Stack-Native Alternatives + +**React**: +```typescript +const [value, setValue] = useState(0); +useEffect(() => { + // Auto-notified on value change +}, [value]); +``` + +**RxJS**: +```typescript +const subject = new BehaviorSubject(0); +subject.subscribe(value => console.log(value)); +subject.next(5); // Notifies subscribers +``` + +**Angular**: +```typescript +private data$ = new BehaviorSubject(initial); +getData() { return this.data$.asObservable(); } +``` + +### Code Smells It Fixes +- **Scattered notification logic**: Centralized in subject +- **Tight coupling**: Observers don't know about each other + +### Common Mistakes +- **Memory leaks**: Forgetting to unsubscribe/detach +- **Notification storms**: Too many updates triggering cascades +- **Order dependency**: Observers should be independent + +--- + +## State + +### Definition +Allows an object to alter its behavior when its internal state changes, appearing to change its class. + +### When to Use +- [x] Object behavior depends on its state +- [x] Operations have large conditional statements that depend on state +- [x] State transitions are well-defined + +### TypeScript Signature +```typescript +// State interface +interface State { + handle(context: Context): void; +} + +// Context +class Context { + private state: State; + + constructor(initialState: State) { + this.state = initialState; + } + + setState(state: State): void { + console.log(`Context: Transitioning to ${state.constructor.name}`); + this.state = state; + } + + request(): void { + this.state.handle(this); + } +} + +// Concrete states +class ConcreteStateA implements State { + handle(context: Context): void { + console.log('StateA handles request'); + context.setState(new ConcreteStateB()); + } +} + +class ConcreteStateB implements State { + handle(context: Context): void { + console.log('StateB handles request'); + context.setState(new ConcreteStateA()); + } +} + +// Usage +const context = new Context(new ConcreteStateA()); +context.request(); // StateA handles request, transitions to StateB +context.request(); // StateB handles request, transitions to StateA +``` + +### Real-World: Document States +```typescript +interface DocumentState { + publish(doc: Document): void; + review(doc: Document): void; +} + +class Draft implements DocumentState { + publish(doc: Document): void { + console.log('Cannot publish draft directly'); + } + review(doc: Document): void { + console.log('Sending for review'); + doc.setState(new InReview()); + } +} + +class InReview implements DocumentState { + publish(doc: Document): void { + console.log('Publishing document'); + doc.setState(new Published()); + } + review(doc: Document): void { + console.log('Already in review'); + } +} + +class Published implements DocumentState { + publish(doc: Document): void { + console.log('Already published'); + } + review(doc: Document): void { + console.log('Cannot review published document'); + } +} + +class Document { + private state: DocumentState = new Draft(); + + setState(state: DocumentState): void { + this.state = state; + } + + publish(): void { + this.state.publish(this); + } + + review(): void { + this.state.review(this); + } +} +``` + +### Stack-Native: React useReducer +```typescript +const reducer = (state: State, action: Action) => { + switch (action.type) { + case 'DRAFT': return { status: 'draft' }; + case 'REVIEW': return { status: 'review' }; + case 'PUBLISHED': return { status: 'published' }; + } +}; + +const [state, dispatch] = useReducer(reducer, { status: 'draft' }); +``` + +### Code Smells It Fixes +- **Complex conditionals on state**: Each state is a separate class +- **Scattered state-dependent behavior**: Localized in state classes + +--- + +## Strategy + +### Definition +Defines a family of algorithms, encapsulates each one, and makes them interchangeable, letting the algorithm vary independently from clients. + +### When to Use +- [x] Many related classes differ only in behavior +- [x] Need different variants of an algorithm +- [x] Algorithm uses data clients shouldn't know about +- [x] Class has multiple conditional statements for selecting behavior + +### TypeScript Signature +```typescript +// Strategy interface +interface Strategy { + execute(a: number, b: number): number; +} + +// Concrete strategies +class AddStrategy implements Strategy { + execute(a: number, b: number): number { + return a + b; + } +} + +class MultiplyStrategy implements Strategy { + execute(a: number, b: number): number { + return a * b; + } +} + +// Context +class Calculator { + constructor(private strategy: Strategy) {} + + setStrategy(strategy: Strategy): void { + this.strategy = strategy; + } + + calculate(a: number, b: number): number { + return this.strategy.execute(a, b); + } +} + +// Usage +const calculator = new Calculator(new AddStrategy()); +console.log(calculator.calculate(5, 3)); // 8 + +calculator.setStrategy(new MultiplyStrategy()); +console.log(calculator.calculate(5, 3)); // 15 +``` + +### Stack-Native: React Hooks +```typescript +// Strategies as hooks +const useCreditPayment = () => ({ process: async (amount) => { /* ... */ } }); +const usePaypalPayment = () => ({ process: async (amount) => { /* ... */ } }); + +const usePaymentStrategy = (type: PaymentType) => { + const strategies = { + credit: useCreditPayment(), + paypal: usePaypalPayment(), + }; + return strategies[type]; +}; + +// Usage in component +const PaymentForm = ({ type }: Props) => { + const strategy = usePaymentStrategy(type); + const handlePay = () => strategy.process(amount); +}; +``` + +### Code Smells It Fixes +- **Switch on type**: `switch (type) { case 'A': ... case 'B': ... }` + → Replace with strategy selection +- **Hardcoded algorithms**: Strategies are interchangeable + +### Common Mistakes +- **Strategy explosion**: Too many small strategies +- **Client awareness**: Client shouldn't know strategy details + +--- + +## Template Method + +### Definition +Defines the skeleton of an algorithm in a method, deferring some steps to subclasses, letting subclasses redefine certain steps without changing structure. + +### When to Use +- [x] Implement invariant parts of algorithm once, leave varying parts to subclasses +- [x] Common behavior among subclasses should be factored and localized +- [x] Control subclass extensions (hook operations) + +### TypeScript Signature +```typescript +abstract class AbstractClass { + // Template method + templateMethod(): void { + this.baseOperation1(); + this.requiredOperation1(); + this.baseOperation2(); + this.hook(); + this.requiredOperation2(); + } + + // Implemented operations + baseOperation1(): void { + console.log('AbstractClass: base operation 1'); + } + + baseOperation2(): void { + console.log('AbstractClass: base operation 2'); + } + + // Must be implemented by subclasses + abstract requiredOperation1(): void; + abstract requiredOperation2(): void; + + // Hook (optional override) + hook(): void { + // Default empty implementation + } +} + +class ConcreteClassA extends AbstractClass { + requiredOperation1(): void { + console.log('ConcreteClassA: operation 1'); + } + + requiredOperation2(): void { + console.log('ConcreteClassA: operation 2'); + } + + hook(): void { + console.log('ConcreteClassA: hook override'); + } +} + +class ConcreteClassB extends AbstractClass { + requiredOperation1(): void { + console.log('ConcreteClassB: operation 1'); + } + + requiredOperation2(): void { + console.log('ConcreteClassB: operation 2'); + } +} + +// Usage +const classA = new ConcreteClassA(); +classA.templateMethod(); +``` + +### Code Smells It Fixes +- **Duplicated algorithm structure**: Template defines common steps +- **Inconsistent step order**: Template enforces order + +--- + +## Visitor + +### Definition +Represents an operation to be performed on elements of an object structure, letting you define new operations without changing classes of elements. + +### When to Use +- [x] Object structure contains many classes with differing interfaces +- [x] Many distinct operations need to be performed on objects +- [x] Object structure rarely changes but operations on it often do + +### TypeScript Signature +```typescript +// Element interface +interface Element { + accept(visitor: Visitor): void; +} + +// Concrete elements +class ConcreteElementA implements Element { + accept(visitor: Visitor): void { + visitor.visitConcreteElementA(this); + } + + operationA(): string { + return 'A'; + } +} + +class ConcreteElementB implements Element { + accept(visitor: Visitor): void { + visitor.visitConcreteElementB(this); + } + + operationB(): string { + return 'B'; + } +} + +// Visitor interface +interface Visitor { + visitConcreteElementA(element: ConcreteElementA): void; + visitConcreteElementB(element: ConcreteElementB): void; +} + +// Concrete visitor +class ConcreteVisitor implements Visitor { + visitConcreteElementA(element: ConcreteElementA): void { + console.log(`Visiting A: ${element.operationA()}`); + } + + visitConcreteElementB(element: ConcreteElementB): void { + console.log(`Visiting B: ${element.operationB()}`); + } +} + +// Usage +const elements: Element[] = [ + new ConcreteElementA(), + new ConcreteElementB(), +]; + +const visitor = new ConcreteVisitor(); +for (const element of elements) { + element.accept(visitor); +} +``` + +### Code Smells It Fixes +- **Adding new operations requires modifying elements**: Visitor externalizes operations +- **Operations scattered across classes**: Visitor groups related operations + +### Common Mistakes +- **Adding new element types**: Requires modifying all visitors (rigid) +- **Breaking encapsulation**: Visitor may need access to internals + +--- + +## Interpreter + +### Definition +Defines a representation for a grammar along with an interpreter that uses the representation to interpret sentences in the language. + +### When to Use +- [x] Grammar is simple (for complex grammars, use parser generators) +- [x] Efficiency is not critical +- [x] Building a simple domain-specific language (DSL) + +### TypeScript Signature +```typescript +// Context +class Context { + constructor(public input: string) {} +} + +// Abstract expression +interface Expression { + interpret(context: Context): number; +} + +// Terminal expression +class NumberExpression implements Expression { + constructor(private value: number) {} + + interpret(context: Context): number { + return this.value; + } +} + +// Non-terminal expressions +class AddExpression implements Expression { + constructor(private left: Expression, private right: Expression) {} + + interpret(context: Context): number { + return this.left.interpret(context) + this.right.interpret(context); + } +} + +class MultiplyExpression implements Expression { + constructor(private left: Expression, private right: Expression) {} + + interpret(context: Context): number { + return this.left.interpret(context) * this.right.interpret(context); + } +} + +// Usage: (5 + 3) * 2 +const context = new Context('(5 + 3) * 2'); +const expression = new MultiplyExpression( + new AddExpression( + new NumberExpression(5), + new NumberExpression(3) + ), + new NumberExpression(2) +); + +console.log(expression.interpret(context)); // 16 +``` + +### Code Smells It Fixes +- **Complex parsing logic**: Grammar rules are explicit classes +- **Hardcoded language interpretation**: Extensible grammar + +--- + +## Summary Table + +| Pattern | Complexity | Use Frequency | Main Benefit | +|---------|------------|---------------|--------------| +| Chain of Responsibility | Medium | Medium | Decouple sender from receiver | +| Command | Medium | Medium | Parameterize, queue, undo operations | +| Iterator | Low | High | Sequential access without exposure | +| Mediator | Medium | Medium | Reduce coupling between objects | +| Memento | Medium | Low | Save/restore state | +| Observer | Low | Very High | One-to-many notifications | +| State | Medium | Medium | State-dependent behavior | +| Strategy | Low | High | Interchangeable algorithms | +| Template Method | Medium | Medium | Algorithm skeleton with variants | +| Visitor | High | Low | Operations on object structure | +| Interpreter | High | Very Low | Simple DSL interpretation | + +## Best Practices + +1. **Observer**: Always unsubscribe to prevent memory leaks +2. **Strategy vs State**: Strategy changes behavior externally; State changes internally +3. **Use framework patterns**: React hooks, RxJS, Redux provide these patterns +4. **Command for undo**: Store history of command objects +5. **Chain of Responsibility**: Keep handlers simple, ensure request is handled + +## References + +- *Design Patterns: Elements of Reusable Object-Oriented Software* (Gang of Four) +- [Refactoring Guru: Behavioral Patterns](https://refactoring.guru/design-patterns/behavioral-patterns) +- [RxJS Documentation](https://rxjs.dev/) diff --git a/examples/skills/design-patterns/reference/creational.md b/examples/skills/design-patterns/reference/creational.md new file mode 100644 index 0000000..63d31c1 --- /dev/null +++ b/examples/skills/design-patterns/reference/creational.md @@ -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(defaultConfig); + +export const ConfigProvider = ({ children }: Props) => { + const [config] = useState(() => loadConfig()); + return {children}; +}; +``` + +**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; + setRoof: TRoof extends true ? never : (roof: string) => HouseBuilderState; + 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) diff --git a/examples/skills/design-patterns/reference/patterns-index.yaml b/examples/skills/design-patterns/reference/patterns-index.yaml new file mode 100644 index 0000000..cf5f6f5 --- /dev/null +++ b/examples/skills/design-patterns/reference/patterns-index.yaml @@ -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 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" diff --git a/examples/skills/design-patterns/reference/structural.md b/examples/skills/design-patterns/reference/structural.md new file mode 100644 index 0000000..905c2bf --- /dev/null +++ b/examples/skills/design-patterns/reference/structural.md @@ -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 '1'; + } +} + +// 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

( + Component: React.ComponentType

+): React.ComponentType

{ + return (props: P) => { + const { user } = useAuth(); + if (!user) return ; + return ; + }; +} + +// 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 { + 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 = 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(); + + 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) diff --git a/examples/skills/design-patterns/signatures/code-smells.yaml b/examples/skills/design-patterns/signatures/code-smells.yaml new file mode 100644 index 0000000..e5543a1 --- /dev/null +++ b/examples/skills/design-patterns/signatures/code-smells.yaml @@ -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" diff --git a/examples/skills/design-patterns/signatures/detection-rules.yaml b/examples/skills/design-patterns/signatures/detection-rules.yaml new file mode 100644 index 0000000..cfd78a6 --- /dev/null +++ b/examples/skills/design-patterns/signatures/detection-rules.yaml @@ -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+]*>" + 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" + 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" diff --git a/examples/skills/design-patterns/signatures/stack-patterns.yaml b/examples/skills/design-patterns/signatures/stack-patterns.yaml new file mode 100644 index 0000000..33755a5 --- /dev/null +++ b/examples/skills/design-patterns/signatures/stack-patterns.yaml @@ -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(defaultValue); + export const MyProvider = ({ children }) => ( + {children} + ); + 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 =

(Component: React.ComponentType

) => { + return (props: P) => { + const { user } = useAuth(); + if (!user) return ; + return ; + }; + }; + rationale: "HOCs wrap components to add behavior" + confidence: 0.85 + + mediator: + replacement: "Context API" + example: | + // Centralized state management + const AppContext = createContext(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(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" #