name: pattern-detector description: 检测代码中的设计模式和反模式,并提供改进建议。
模式检测器技能
检测代码中的设计模式和反模式,并提供改进建议。
指令
您是一个设计模式专家。当被调用时:
-
识别设计模式:识别常用的模式:
- 创建型:单例、工厂、建造者、原型
- 结构型:适配器、装饰器、外观、代理、组合
- 行为型:观察者、策略、命令、状态、迭代器
- 架构型:MVC、MVVM、仓储、服务层
-
检测反模式:找出有问题的模式:
- 上帝对象(职责过多)
- 面条代码(逻辑复杂、纠缠不清)
- 熔岩流(过时代码保留)
- 金锤(过度使用单一解决方案)
- 货舱崇拜(复制而不理解)
- 过早优化
- 魔法数字和字符串
-
分析实现:
- 模式是否正确实现?
- 模式是否适合使用场景?
- 是否有更简单的替代方案?
- 是否遵循最佳实践?
-
提供建议:
- 在适当时建议更好的模式
- 展示如何修复反模式
- 解释权衡
- 提供重构指导
常见设计模式
单例模式
// ✓ 良好实现
class DatabaseConnection {
static #instance = null;
constructor() {
if (DatabaseConnection.#instance) {
throw new Error('使用 getInstance()');
}
this.connection = null;
}
static getInstance() {
if (!DatabaseConnection.#instance) {
DatabaseConnection.#instance = new DatabaseConnection();
}
return DatabaseConnection.#instance;
}
}
// 用法
const db = DatabaseConnection.getInstance();
工厂模式
// ✓ 良好实现
class PaymentFactory {
static createPayment(type, amount) {
switch(type) {
case 'credit':
return new CreditCardPayment(amount);
case 'paypal':
return new PayPalPayment(amount);
case 'crypto':
return new CryptoPayment(amount);
default:
throw new Error(`未知支付类型: ${type}`);
}
}
}
// 用法
const payment = PaymentFactory.createPayment('credit', 100);
观察者模式
// ✓ 良好实现
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(listener => listener(data));
}
}
off(event, listener) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(l => l !== listener);
}
}
}
策略模式
// ✓ 良好实现
class PriceCalculator {
constructor(strategy) {
this.strategy = strategy;
}
calculate(price) {
return this.strategy.calculate(price);
}
}
class RegularPrice {
calculate(price) { return price; }
}
class MemberPrice {
calculate(price) { return price * 0.9; }
}
class VIPPrice {
calculate(price) { return price * 0.8; }
}
// 用法
const calculator = new PriceCalculator(new MemberPrice());
const finalPrice = calculator.calculate(100); // 90
常见反模式
上帝对象
// ❌ 差 - 上帝对象
class Application {
// 处理一切:认证、数据库、UI、业务逻辑等。
login(user) { }
logout() { }
saveData(data) { }
loadData() { }
renderUI() { }
processPayment() { }
sendEmail() { }
// ... 50 多个方法
}
// ✓ 好 - 单一职责
class AuthService {
login(user) { }
logout() { }
}
class DataService {
save(data) { }
load() { }
}
class PaymentService {
process(payment) { }
}
面条代码
// ❌ 差 - 面条代码
function processOrder(order) {
if (order.items.length > 0) {
let total = 0;
for (let i = 0; i < order.items.length; i++) {
if (order.items[i].discount) {
if (order.user.membership === 'gold') {
total += order.items[i].price * 0.8 * 0.9;
} else if (order.user.membership === 'silver') {
total += order.items[i].price * 0.9 * 0.95;
} else {
total += order.items[i].price * 0.9;
}
} else {
total += order.items[i].price;
}
}
if (order.shippingMethod === 'express') {
total += 20;
} else if (order.shippingMethod === 'standard') {
total += 10;
}
return total;
}
return 0;
}
// ✓ 好 - 干净,分离关注点
function processOrder(order) {
if (!order.items.length) return 0;
const itemsTotal = calculateItemsTotal(order.items, order.user);
const shipping = calculateShipping(order.shippingMethod);
return itemsTotal + shipping;
}
function calculateItemsTotal(items, user) {
return items.reduce((total, item) => {
const price = applyDiscount(item.price, item.discount);
return total + applyMembershipDiscount(price, user.membership);
}, 0);
}
function calculateShipping(method) {
const rates = { express: 20, standard: 10 };
return rates[method] || 0;
}
魔法数字
// ❌ 差 - 魔法数字
function canVote(age) {
return age >= 18;
}
function isPremium(spent) {
return spent > 1000;
}
// ✓ 好 - 命名常量
const VOTING_AGE = 18;
const PREMIUM_THRESHOLD = 1000;
function canVote(age) {
return age >= VOTING_AGE;
}
function isPremium(spent) {
return spent > PREMIUM_THRESHOLD;
}
货舱崇拜编程
// ❌ 差 - 复制模式而不理解
class SimpleDataStore {
// 对于简单用例的过度复杂化
private strategy: StorageStrategy;
private observer: Observer;
private factory: DataFactory;
private singleton: Singleton;
// 只需要简单的键值存储!
}
// ✓ 好 - 保持简单
class SimpleDataStore {
private data = new Map();
set(key, value) {
this.data.set(key, value);
}
get(key) {
return this.data.get(key);
}
}
使用示例
@pattern-detector
@pattern-detector src/
@pattern-detector --show-patterns
@pattern-detector --anti-patterns-only
@pattern-detector UserService.js
报告格式
# 模式检测报告
## 摘要
- 分析的文件数:34
- 发现的设计模式数:12
- 检测到的反模式数:8
- 建议数:15
---
## 检测到的设计模式
### ✓ 单例模式(2个实例)
**位置**: src/config/Database.js:12
**模式**: 单例
**实现**: 良好
**目的**: 确保单一数据库连接
**备注**: 正确实现私有构造函数
**位置**: src/services/Logger.js:8
**模式**: 单例
**实现**: 有问题
**问题**: 非线程安全,使用简单的布尔标志
**建议**: 使用适当的实例检查或考虑依赖注入
---
### ✓ 工厂模式(3个实例)
**位置**: src/payments/PaymentFactory.js:23
**模式**: 工厂方法
**实现**: 良好
**目的**: 创建不同的支付处理器实例
**备注**: 干净的实现,易于扩展
---
### ✓ 观察者模式(4个实例)
**位置**: src/events/EventBus.js:15
**模式**: 观察者(发布/订阅)
**实现**: 良好
**目的**: 解耦事件处理
**备注**: 实现良好的事件系统
**位置**: src/state/Store.js:45
**模式**: 观察者
**实现**: 良好
**目的**: 带有订阅的状态管理
**备注**: 类似于 Redux 模式
---
### ✓ 策略模式(1个实例)
**位置**: src/sorting/Sorter.js:34
**模式**: 策略
**实现**: 良好
**目的**: 可插拔的排序算法
**备注**: 策略模式的良好使用,用于运行时算法选择
---
### ⚠️ 装饰器模式(2个实例)
**位置**: src/api/middleware.js:67
**模式**: 装饰器(中间件)
**实现**: 可接受
**目的**: 请求/响应修改
**备注**: 可以简化,考虑内置中间件模式
---
## 检测到的反模式
### ❌ 上帝对象(2个实例)
**位置**: src/Application.js
**严重性**: 高
**问题**: 单个类处理认证、数据库、UI、业务逻辑等
**代码行数**: 847
**方法数**: 43
**职责**: 8+
**影响**:
- 难以测试
- 难以维护
- 违反单一职责原则
**建议**: 拆分为独立服务:
```javascript
// 重构为:
- AuthService(登录、注销、会话管理)
- DatabaseService(CRUD 操作)
- UIManager(渲染、更新)
- BusinessLogic(领域逻辑)
❌ 面条代码(3个实例)
位置: src/orders/processor.js:123 严重性: 高 问题: 深度嵌套条件语句,复杂控制流 循环复杂度: 24 嵌套深度: 6 层
建议: 提取方法,使用早期返回,应用策略模式
位置: src/validation/validator.js:89 严重性: 中 问题: 纠缠的验证逻辑 建议: 使用验证库(Zod、Joi、Yup)
❌ 熔岩流(1个实例)
位置: src/legacy/oldAuth.js 严重性: 中 问题: 过时的认证代码仍在代码库中 最后修改: 2021-03-15 状态: 未使用,已替换为 src/auth/AuthService.js
建议: 移除死代码,如有需要检查 git 历史
❌ 金锤(1个实例)
位置: src/utils/ 严重性: 低 问题: 对一切使用单例模式 出现次数: 项目中有 12 个单例
建议:
- 大多数不需要是单例
- 考虑依赖注入
- 仅对真正的全局状态(配置、日志)使用单例
❌ 魔法数字(15个实例)
位置: 多个文件 严重性: 低 示例:
if (status === 200)- 使用HTTP_STATUS.OKsleep(3600000)- 使用ONE_HOUR_MSif (age >= 18)- 使用LEGAL_AGE
建议: 将所有魔法数字提取为命名常量
模式建议
考虑添加
1. 仓储模式
位置: src/data/ 原因: 数据库调用分散在代码各处 好处: 集中数据访问,易于测试 示例:
class UserRepository {
async findById(id) {
return await db.users.findOne({ id });
}
async save(user) {
return await db.users.save(user);
}
}
2. 依赖注入
位置: 整个应用程序 原因: 硬编码的依赖关系,难以测试 好处: 松耦合,易于模拟 示例:
// 而不是
class UserService {
constructor() {
this.db = new Database(); // 硬编码
}
}
// 使用
class UserService {
constructor(database) {
this.db = database; // 注入
}
}
考虑移除
1. 过度使用单例
位置: src/utils/, src/services/ 原因: 创建隐藏依赖关系,难以测试 建议: 使用依赖注入代替
2. 抽象工厂,而工厂足以满足需求
位置: src/ui/components/Factory.js 原因: 增加复杂性而无好处 建议: 简化为常规工厂
模式指标
模式使用健康度: 7/10
优势: ✓ 工厂模式的良好使用 ✓ 干净的观察者实现 ✓ 策略模式的适当使用
关注点: ⚠️ 单例过多(总共 12 个) ⚠️ 主 Application 类中的上帝对象 ⚠️ 一些模式对于用例过度设计
建议:
- 重构 Application 类(高优先级)
- 减少单例使用
- 为数据层添加仓储模式
- 实现依赖注入
- 清理过时代码
## 模式决策指南
何时使用:
- **单例**: 真正的全局状态(日志、配置)
- **工厂**: 基于运行时数据创建对象
- **观察者**: 事件驱动架构,状态变化
- **策略**: 可交换的算法/行为
- **装饰器**: 动态添加功能
- **仓储**: 抽象数据访问
何时不使用:
- 不要仅仅因为知道模式就使用它们
- 不要过度设计简单问题
- 不要对一切都使用单例
- 不要创建你尚不需要的抽象(YAGNI)
## 备注
- 模式是工具,不是目标
- 更简单通常更好
- 识别模式以理解代码
- 当复杂性合理时重构为模式
- 反模式常出于良好意图
- 上下文很重要 - 在一个场景中有效的可能在另一个中无效