design-pattern
npx skills add https://github.com/projanvil/mindforge --skill design-pattern
Agent 安装分布
Skill 文档
Design Pattern Mastery
Instructions
You are an expert in software design patterns with deep knowledge of:
- Gang of Four (GoF) design patterns
- Architectural patterns (MVC, MVVM, Clean Architecture, Hexagonal Architecture)
- Modern patterns (Dependency Injection, Repository, CQRS, Event Sourcing)
- Anti-patterns and code smells
- SOLID principles and design best practices
What This Skill Does
This skill provides comprehensive knowledge and practical guidance on software design patterns. It helps you:
- Identify appropriate patterns for specific problems
- Implement patterns correctly in various programming languages
- Recognize code smells and anti-patterns
- Apply SOLID principles
- Balance flexibility with simplicity
- Refactor code to improve maintainability
When to Use This Skill
Use this skill when:
- Designing new software components or systems
- Reviewing code for design quality
- Refactoring existing code
- Resolving complex design problems
- Explaining design decisions
- Teaching software engineering concepts
- Identifying code smells and suggesting improvements
Core Competencies
1. Creational Patterns
Singleton Pattern
- Ensure a class has only one instance
- Provide global access point
- Use for configuration managers, connection pools, logging
public class DatabaseConnection {
private static volatile DatabaseConnection instance;
private DatabaseConnection() {}
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (DatabaseConnection.class) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
}
Factory Method Pattern
- Define interface for creating objects
- Let subclasses decide which class to instantiate
- Use when exact types aren’t known until runtime
interface Product {
operation(): string;
}
abstract class Creator {
abstract factoryMethod(): Product;
someOperation(): string {
const product = this.factoryMethod();
return product.operation();
}
}
Builder Pattern
- Construct complex objects step by step
- Separate construction from representation
- Use for objects with many optional parameters
class User:
def __init__(self):
self.name = None
self.email = None
self.age = None
class UserBuilder:
def __init__(self):
self.user = User()
def with_name(self, name):
self.user.name = name
return self
def with_email(self, email):
self.user.email = email
return self
def build(self):
return self.user
Prototype Pattern
- Clone existing objects without coupling to their classes
- Use when object creation is expensive
Abstract Factory Pattern
- Provide interface for creating families of related objects
- Use when system should be independent of how objects are created
2. Structural Patterns
Adapter Pattern
- Convert interface of a class into another interface
- Allow incompatible interfaces to work together
type LegacyPrinter interface {
PrintOld(text string)
}
type ModernPrinter interface {
Print(text string)
}
type PrinterAdapter struct {
legacy LegacyPrinter
}
func (a *PrinterAdapter) Print(text string) {
a.legacy.PrintOld(text)
}
Decorator Pattern
- Attach additional responsibilities to objects dynamically
- Provide flexible alternative to subclassing
interface Component {
operation(): string;
}
class ConcreteComponent implements Component {
operation(): string {
return "ConcreteComponent";
}
}
class Decorator implements Component {
protected component: Component;
constructor(component: Component) {
this.component = component;
}
operation(): string {
return this.component.operation();
}
}
class ConcreteDecorator extends Decorator {
operation(): string {
return `ConcreteDecorator(${super.operation()})`;
}
}
Facade Pattern
- Provide unified interface to a set of interfaces
- Simplify complex subsystem
Proxy Pattern
- Provide placeholder for another object
- Control access, lazy initialization, logging
Composite Pattern
- Compose objects into tree structures
- Treat individual objects and compositions uniformly
Bridge Pattern
- Decouple abstraction from implementation
- Both can vary independently
Flyweight Pattern
- Share common state between multiple objects
- Reduce memory footprint
3. Behavioral Patterns
Strategy Pattern
- Define family of algorithms
- Make them interchangeable
- Use when you need different variants of an algorithm
interface PaymentStrategy {
void pay(int amount);
}
class CreditCardStrategy implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card");
}
}
class PayPalStrategy implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal");
}
}
class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}
Observer Pattern
- Define one-to-many dependency
- When one object changes, notify dependents
- Use for event handling systems
class Subject:
def __init__(self):
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def notify(self, event):
for observer in self._observers:
observer.update(event)
class Observer:
def update(self, event):
pass
Command Pattern
- Encapsulate request as an object
- Parameterize clients with different requests
- Support undo/redo operations
State Pattern
- Allow object to alter behavior when internal state changes
- Object appears to change its class
Template Method Pattern
- Define skeleton of algorithm in base class
- Let subclasses override specific steps
Iterator Pattern
- Access elements of aggregate sequentially
- Without exposing underlying representation
Mediator Pattern
- Define object that encapsulates how objects interact
- Reduce coupling between components
Memento Pattern
- Capture and externalize object’s internal state
- Allow object restoration later
Chain of Responsibility Pattern
- Pass request along chain of handlers
- Each handler decides to process or pass on
Visitor Pattern
- Separate algorithm from object structure
- Add new operations without modifying objects
4. Architectural Patterns
Model-View-Controller (MVC)
- Separate concerns: data (Model), presentation (View), logic (Controller)
- Use for web applications, desktop applications
Model-View-ViewModel (MVVM)
- Separate UI from business logic
- Use data binding between View and ViewModel
- Popular in WPF, Angular, Vue.js
Clean Architecture
- Dependency rule: dependencies point inward
- Entities â Use Cases â Interface Adapters â Frameworks
- Independent of frameworks, UI, database
Hexagonal Architecture (Ports and Adapters)
- Application core isolated from external concerns
- Ports define interfaces, Adapters implement them
- Easy to test, swap implementations
Repository Pattern
- Mediate between domain and data mapping layers
- Provides collection-like interface for accessing domain objects
interface UserRepository {
findById(id: string): Promise<User>;
save(user: User): Promise<void>;
delete(id: string): Promise<void>;
}
class UserRepositoryImpl implements UserRepository {
async findById(id: string): Promise<User> {
// Database access logic
}
async save(user: User): Promise<void> {
// Save logic
}
}
CQRS (Command Query Responsibility Segregation)
- Separate read and write operations
- Different models for updates and queries
- Improves scalability and performance
Event Sourcing
- Store state as sequence of events
- Rebuild current state by replaying events
- Provides audit trail, time travel
5. Modern Patterns
Dependency Injection
- Inversion of Control principle
- Dependencies provided from outside
- Improves testability, flexibility
public class UserService {
private final UserRepository repository;
// Constructor injection
public UserService(UserRepository repository) {
this.repository = repository;
}
}
Service Locator
- Central registry for obtaining services
- Alternative to Dependency Injection
Null Object Pattern
- Provide default object instead of null
- Eliminates null checks
class User:
def get_name(self):
pass
class RealUser(User):
def __init__(self, name):
self.name = name
def get_name(self):
return self.name
class NullUser(User):
def get_name(self):
return "Guest"
Object Pool Pattern
- Reuse expensive-to-create objects
- Manage pool of reusable objects
Circuit Breaker Pattern
- Prevent cascading failures
- Fail fast when service is unavailable
SOLID Principles
Single Responsibility Principle (SRP)
- A class should have one reason to change
- Each class does one thing well
Open/Closed Principle (OCP)
- Open for extension, closed for modification
- Use abstractions and polymorphism
Liskov Substitution Principle (LSP)
- Subtypes must be substitutable for their base types
- Derived classes must not break base class contracts
Interface Segregation Principle (ISP)
- Clients shouldn’t depend on interfaces they don’t use
- Create specific interfaces rather than general ones
Dependency Inversion Principle (DIP)
- Depend on abstractions, not concretions
- High-level modules shouldn’t depend on low-level modules
Anti-Patterns to Avoid
God Object
- Class knows too much or does too much
- Violates SRP
Spaghetti Code
- Tangled control flow
- Difficult to understand and maintain
Lava Flow
- Dead code that’s never removed
- Fear of breaking something
Golden Hammer
- Overusing one pattern for everything
- “When you have a hammer, everything looks like a nail”
Premature Optimization
- Optimizing before identifying bottlenecks
- Makes code complex unnecessarily
Cargo Cult Programming
- Using patterns without understanding why
- Copying code without comprehension
Pattern Selection Guide
When to use Creational Patterns:
- Object creation is complex
- Need to control instance creation
- Want to decouple creation from usage
When to use Structural Patterns:
- Need to compose objects
- Want to adapt interfaces
- Need to simplify complex systems
When to use Behavioral Patterns:
- Need to define communication between objects
- Want to encapsulate algorithms
- Need flexibility in object behavior
Examples
Example 1: Refactoring to Strategy Pattern
Before:
class PaymentProcessor {
public void processPayment(String type, int amount) {
if (type.equals("credit")) {
// Credit card logic
} else if (type.equals("paypal")) {
// PayPal logic
} else if (type.equals("crypto")) {
// Crypto logic
}
}
}
After:
interface PaymentStrategy {
void pay(int amount);
}
class PaymentProcessor {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void processPayment(int amount) {
strategy.pay(amount);
}
}
Example 2: Implementing Repository Pattern
// Domain entity
class User {
constructor(
public id: string,
public name: string,
public email: string
) {}
}
// Repository interface
interface UserRepository {
findById(id: string): Promise<User | null>;
findAll(): Promise<User[]>;
save(user: User): Promise<void>;
delete(id: string): Promise<void>;
}
// Implementation with PostgreSQL
class PostgresUserRepository implements UserRepository {
async findById(id: string): Promise<User | null> {
const result = await db.query('SELECT * FROM users WHERE id = $1', [id]);
return result.rows[0] ? new User(result.rows[0].id, result.rows[0].name, result.rows[0].email) : null;
}
async save(user: User): Promise<void> {
await db.query(
'INSERT INTO users (id, name, email) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET name = $2, email = $3',
[user.id, user.name, user.email]
);
}
}
Best Practices
- Understand the Problem First – Don’t force patterns where they don’t fit. Patterns are solutions to recurring problems.
- Keep It Simple – Start simple, add patterns as needed. Don’t over-engineer.
- Name Things Clearly – Use pattern names in class/method names when appropriate. Makes code self-documenting.
- Combine Patterns Thoughtfully – Patterns often work together (Factory + Singleton, Strategy + Template Method, etc.)
- Consider Trade-offs – Patterns add complexity. Balance flexibility vs. simplicity.
- Test-Driven Development – Write tests first. Patterns emerge naturally from refactoring.
- Refactor to Patterns – Don’t design patterns upfront. Let them emerge as code evolves.
Code Review Checklist
When reviewing code for pattern usage:
- Is the pattern appropriate for the problem?
- Is the implementation correct?
- Does it improve code quality?
- Is it over-engineering?
- Are SOLID principles followed?
- Is the code testable?
- Is it well-documented?
- Are there simpler alternatives?
Pattern Reference Quick Guide
| Problem | Pattern | Use When |
|---|---|---|
| Single instance needed | Singleton | Global state, resource management |
| Complex object creation | Builder | Many optional parameters |
| Family of related objects | Abstract Factory | Need consistent object families |
| Clone existing objects | Prototype | Object creation is expensive |
| Interface mismatch | Adapter | Integrating legacy code |
| Add responsibilities | Decorator | Need flexible extensions |
| Simplify complex system | Facade | Need simplified interface |
| Control access | Proxy | Lazy loading, access control |
| Interchangeable algorithms | Strategy | Multiple algorithm variants |
| Notify dependents | Observer | Event handling, pub-sub |
| Encapsulate requests | Command | Undo/redo, queuing operations |
| State-dependent behavior | State | Complex state transitions |
Notes
Design patterns are proven solutions to common problems in software design. Use them to:
- Write maintainable, extensible code
- Communicate design intent clearly
- Leverage collective wisdom of software community
- Avoid reinventing the wheel
Remember: Patterns are tools, not rules. Use judgment to apply them appropriately in your specific context. The best code is often the simplest code that solves the problem effectively.