您现在的位置是:网站首页 > 模式重构的识别与实施步骤文章详情
模式重构的识别与实施步骤
陈川
【
JavaScript
】
33717人已围观
6001字
模式重构的识别与实施步骤
重构是提升代码质量的重要手段,而模式重构则是将现有代码向设计模式靠拢的过程。识别代码中需要重构的部分并正确实施模式转换,能够显著提高代码的可维护性和扩展性。下面详细探讨模式重构的识别方法和具体实施步骤。
识别需要重构的代码模式
识别需要重构的代码是模式重构的第一步。常见需要重构的代码特征包括:
- 重复代码:相同或相似的代码出现在多个地方
// 重构前
function calculateTotal(items) {
let total = 0;
for (let item of items) {
total += item.price * item.quantity;
}
return total;
}
function calculateTax(items) {
let total = 0;
for (let item of items) {
total += item.price * item.quantity;
}
return total * 0.1;
}
- 过长的函数或类:一个函数或类承担了太多职责
- 复杂的条件逻辑:多层嵌套的if-else或switch语句
- 过度使用基本类型:用基本类型表示复杂概念
- 数据泥团:总是同时出现的一组数据
选择合适的设计模式
根据识别出的问题,选择合适的设计模式:
- 创建型问题:考虑工厂模式、建造者模式、单例模式
- 结构型问题:考虑适配器模式、装饰器模式、外观模式
- 行为型问题:考虑策略模式、观察者模式、状态模式
例如,对于前面重复代码的例子,可以应用策略模式:
// 重构后 - 策略模式
class CalculationStrategy {
calculate(items) {
throw new Error('必须实现calculate方法');
}
}
class TotalCalculation extends CalculationStrategy {
calculate(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
}
class TaxCalculation extends CalculationStrategy {
calculate(items) {
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
return total * 0.1;
}
}
function calculate(strategy, items) {
return strategy.calculate(items);
}
实施重构的步骤
1. 建立测试保护网
在开始重构前,确保有足够的测试覆盖:
describe('Calculation Tests', () => {
const items = [
{ price: 10, quantity: 2 },
{ price: 15, quantity: 1 }
];
it('should calculate total correctly', () => {
expect(calculate(new TotalCalculation(), items)).toEqual(35);
});
it('should calculate tax correctly', () => {
expect(calculate(new TaxCalculation(), items)).toEqual(3.5);
});
});
2. 小步重构
每次只做一个小改动,确保测试通过后再继续:
// 第一步:提取重复的循环逻辑
function calculateSubtotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
// 第二步:重构原函数使用新函数
function calculateTotal(items) {
return calculateSubtotal(items);
}
function calculateTax(items) {
return calculateSubtotal(items) * 0.1;
}
3. 应用设计模式
逐步引入设计模式结构:
// 第三步:引入策略接口
interface CalculationStrategy {
calculate(items: Item[]): number;
}
// 第四步:实现具体策略
class TotalStrategy implements CalculationStrategy {
calculate(items: Item[]) {
return calculateSubtotal(items);
}
}
4. 验证和优化
检查重构后的代码:
- 是否消除了原来的问题
- 是否引入了新的复杂度
- 性能是否有显著下降
常见模式重构示例
工厂模式重构
重构前:
function createUser(type) {
switch(type) {
case 'admin':
return { role: 'admin', permissions: ['read', 'write', 'delete'] };
case 'editor':
return { role: 'editor', permissions: ['read', 'write'] };
case 'guest':
return { role: 'guest', permissions: ['read'] };
default:
throw new Error('Invalid user type');
}
}
重构后:
class UserFactory {
static create(type) {
const creators = {
admin: () => new AdminUser(),
editor: () => new EditorUser(),
guest: () => new GuestUser()
};
if (creators[type]) {
return creators[type]();
}
throw new Error('Invalid user type');
}
}
class AdminUser {
constructor() {
this.role = 'admin';
this.permissions = ['read', 'write', 'delete'];
}
}
观察者模式重构
重构前:
class Stock {
constructor() {
this.price = 0;
this.investors = [];
}
setPrice(price) {
this.price = price;
this.notifyInvestors();
}
notifyInvestors() {
this.investors.forEach(investor => {
if (investor.type === 'email') {
sendEmail(investor.address, this.price);
} else if (investor.type === 'sms') {
sendSMS(investor.phone, this.price);
}
});
}
}
重构后:
class Stock {
constructor() {
this.price = 0;
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
setPrice(price) {
this.price = price;
this.notifyObservers();
}
notifyObservers() {
this.observers.forEach(observer => observer.update(this.price));
}
}
class EmailNotifier {
update(price) {
sendEmail(this.address, price);
}
}
class SMSNotifier {
update(price) {
sendSMS(this.phone, price);
}
}
重构中的注意事项
- 保持功能不变:重构不应改变代码的外部行为
- 小步前进:每次只做一个小改动,立即测试
- 版本控制:频繁提交,便于回退
- 文档更新:同步更新相关文档和注释
- 性能考量:某些模式可能带来性能开销
重构后的代码评估
评估重构是否成功的标准:
- 可读性:代码是否更易于理解
- 可维护性:修改需求时是否更容易
- 可扩展性:添加新功能是否更简单
- 解耦程度:组件间的依赖是否减少
- 复用性:代码是否更容易被复用
例如,观察者模式重构后的评估:
- 新增通知方式只需添加新Observer类
- Stock类不再需要了解具体通知方式
- 各通知方式的实现细节被封装
- 测试可以针对每个Observer单独进行
模式重构的进阶技巧
- 模式组合:结合多个模式解决复杂问题
// 结合工厂模式和策略模式
class PaymentProcessor {
constructor(strategyFactory) {
this.strategyFactory = strategyFactory;
}
process(paymentType, amount) {
const strategy = this.strategyFactory.create(paymentType);
return strategy.execute(amount);
}
}
- 模式参数化:通过配置动态选择模式
- 模式简化:根据实际需求简化经典模式实现
- 模式演变:从一个简单模式逐步演变为更复杂的模式
重构与性能的平衡
某些设计模式可能带来性能开销,需要权衡:
- 代理模式:增加间接层带来的开销
- 装饰器模式:多层包装的函数调用
- 观察者模式:通知所有观察者的时间成本
优化策略:
// 观察者模式优化 - 批量通知
class Observable {
constructor() {
this.observers = [];
this.changed = false;
}
setChanged() {
this.changed = true;
}
notifyObservers() {
if (this.changed) {
this.observers.forEach(o => o.update(this));
this.changed = false;
}
}
}
重构工具的支持
利用工具辅助重构:
- IDE重构功能:
- 重命名
- 提取方法
- 内联变量
- 静态分析工具:
- ESLint模式检测
- SonarQube代码质量分析
- 测试工具:
- Jest快照测试
- Cypress端到端测试
团队协作中的模式重构
- 代码审查:重构代码需要特别关注
- 文档记录:记录重构决策和模式选择原因
- 知识共享:通过技术分享传播模式知识
- 渐进式重构:大型重构分阶段进行
模式重构的常见误区
- 过度设计:在不必要的地方应用模式
- 模式滥用:强迫使用模式导致代码复杂化
- 忽视上下文:不考虑项目规模和生命周期
- 模式教条:严格遵循模式定义而忽视实际需求
持续重构的文化
- 小规模持续重构:修复小问题时顺便重构
- 技术债务管理:定期安排重构任务
- 重构指标:跟踪代码质量指标变化
- 自动化检测:设置代码质量门禁
上一篇: 测试驱动开发(TDD)与设计模式
下一篇: 代码异味与设计模式重构机会