引言:代码重构的重要性与价值
代码重构是在不改变软件外部行为的前提下,改善其内部结构的过程。对于开发者而言,掌握重构技巧不仅是提升代码质量的关键,更是提高开发效率、降低维护成本的核心能力。随着项目规模的扩大和时间的推移,代码库会逐渐变得复杂和难以维护,此时重构就显得尤为重要。
重构的核心价值体现在以下几个方面:
- 提升代码可读性:清晰的代码结构让团队成员更容易理解和维护
- 降低技术债务:定期重构避免小问题累积成难以解决的技术债务
- 提高开发效率:良好的代码结构让新功能开发和bug修复更加高效
- 便于扩展和维护:合理的架构设计为系统演进提供坚实基础
- 减少bug产生:清晰的逻辑和职责划分能有效减少错误
本文将从基础概念开始,逐步深入到高级技巧,通过完整的代码示例详细说明重构的各个方面,帮助开发者系统性地掌握代码优化技能。
第一部分:重构基础概念与准备
1.1 重构的基本原则
重构的核心原则是”小步前进,频繁测试”。每次重构都应该保持以下原则:
- 保持行为不变:重构不应该改变程序的可观察行为
- 测试先行:确保有可靠的测试覆盖,这是重构的安全网
- 小步前进:每次只做小的改动,便于定位问题
- 频繁提交:每个小的重构步骤都应该单独提交
1.2 识别需要重构的代码信号
以下是一些常见的需要重构的代码”坏味道”:
- 重复代码:相同的逻辑在多处出现
- 过长函数:函数超过50行,难以理解
- 过大的类:类承担了过多职责
- 过长参数列表:函数参数超过3-4个
- 过度使用全局变量:难以追踪状态变化
- 复杂的条件嵌套:多层if-else嵌套难以理解
- 魔法数字和字符串:代码中直接出现的数字或字符串
- 过深的继承层次:继承关系复杂难以维护
1.3 重构工具与环境准备
在进行重构前,需要准备以下工具:
- 版本控制系统:Git是最常用的选择
- 自动化测试框架:确保重构安全
- IDE支持:现代IDE(如VS Code、IntelliJ IDEA)提供强大的重构工具
- 静态分析工具:ESLint、SonarQube等帮助发现代码问题
第二部分:基础重构技巧详解
2.1 提取函数(Extract Function)
这是最常用的重构技巧,将复杂代码块提取为独立函数。
重构前:
function processOrder(order) {
// 验证订单
if (!order.id) {
throw new Error('订单ID不能为空');
}
if (!order.items || order.items.length === 0) {
throw new Error('订单项不能为空');
}
// 计算总价
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
// 应用折扣
if (order.customerType === 'VIP') {
total *= 0.9; // VIP 9折
} else if (order.customerType === 'SVIP') {
total *= 0.8; // SVIP 8折
}
// 计算税费
const tax = total * 0.1;
const finalAmount = total + tax;
// 保存到数据库
db.save('orders', {
...order,
total: finalAmount,
tax: tax,
status: 'processed'
});
return finalAmount;
}
重构后:
function processOrder(order) {
validateOrder(order);
const total = calculateTotal(order.items);
const discountedTotal = applyDiscount(total, order.customerType);
const finalAmount = calculateFinalAmount(discountedTotal);
saveOrder(order, finalAmount);
return finalAmount;
}
function validateOrder(order) {
if (!order.id) {
throw new Error('订单ID不能为空');
}
if (!order.items || order.items.length === 0) {
throw new Error('订单项不能为空');
}
}
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
function applyDiscount(total, customerType) {
const discountMap = {
'VIP': 0.9,
'SVIP': 0.8
};
const discount = discountMap[customerType] || 1;
return total * discount;
}
function calculateFinalAmount(total) {
const tax = total * 0.1;
return total + tax;
}
function saveOrder(order, finalAmount) {
db.save('orders', {
...order,
total: finalAmount,
tax: finalAmount * 0.1,
status: 'processed'
});
}
重构收益:
- 每个函数职责单一,易于理解和测试
- 可以独立复用某些计算逻辑
- 代码可读性大幅提升
2.2 内联函数(Inline Function)
当函数过于简单或不再需要时,可以将其内联到调用处。
重构前:
function getDiscountRate(customerType) {
return customerType === 'VIP' ? 0.9 : 1;
}
function calculatePrice(price, customerType) {
return price * getDiscountRate(customerType);
}
重构后:
function calculatePrice(price, customerType) {
const discountRate = customerType === 'VIP' ? 0.9 : 1;
return price * discountRate;
}
2.3 提取变量(Extract Variable)
将复杂表达式提取为有意义的变量。
重构前:
function calculateOrderTotal(order) {
return order.items.reduce((sum, item) =>
sum + item.price * item.quantity * (1 + order.taxRate), 0);
}
重构后:
function calculateOrderTotal(order) {
return order.items.reduce((sum, item) => {
const itemTotal = item.price * item.quantity;
const itemWithTax = itemTotal * (1 + order.taxRate);
return sum + itemWithTax;
}, 0);
}
2.4 替换魔法数字(Replace Magic Number with Symbolic Constant)
用命名常量替换代码中的字面量。
重构前:
function calculateFinalPrice(price) {
const discount = price * 0.8; // SVIP折扣
const tax = discount * 0.1; // 税率
return discount + tax;
}
重构后:
const SVIP_DISCOUNT = 0.8;
const TAX_RATE = 0.1;
function calculateFinalPrice(price) {
const discount = price * SVIP_DISCOUNT;
const tax = discount * TAX_RATE;
return discount + tax;
}
2.5 改变函数声明(Change Function Declaration)
调整函数签名使其更清晰。
重构前:
function createOrder(items, customerId, isVIP, address) {
// ... 实现
}
重构后:
function createOrder({ items, customerId, isVIP, address }) {
// ... 实现
}
// 调用方式
createOrder({
items: [...],
customerId: 'C001',
isVIP: true,
address: '...'
});
第三部分:面向对象重构技巧
3.1 提取类(Extract Class)
当一个类承担过多职责时,将其拆分为多个类。
重构前:
class User {
constructor(name, email, street, city, zipCode) {
this.name = name;
this.email = email;
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
validateEmail() {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email);
}
getFullAddress() {
return `${this.street}, ${this.city}, ${this.zipCode}`;
}
saveToDatabase() {
// 保存用户信息到数据库
}
}
重构后:
class Address {
constructor(street, city, zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
getFullAddress() {
return `${this.street}, ${this.city}, ${this.zipCode}`;
}
}
class User {
constructor(name, email, address) {
this.name = name;
this.email = email;
this.address = address;
}
validateEmail() {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email);
}
saveToDatabase() {
// 保存用户信息到数据库
}
}
3.2 提取接口(Extract Interface)
为多个类定义共同的契约。
重构前:
class PDFGenerator {
generate(content) {
// PDF生成逻辑
}
}
class ExcelGenerator {
generate(content) {
// Excel生成逻辑
}
}
function exportReport(generator, content) {
generator.generate(content);
}
重构后:
// 定义接口
class ReportGenerator {
generate(content) {
throw new Error('必须实现generate方法');
}
}
class PDFGenerator extends ReportGenerator {
generate(content) {
// PDF生成逻辑
}
}
class ExcelGenerator extends ReportGenerator {
generate(content) {
// Excel生成逻辑
}
}
function exportReport(generator, content) {
if (!(generator instanceof ReportGenerator)) {
throw new Error('必须提供ReportGenerator实例');
}
generator.generate(content);
}
3.3 用多态替换条件表达式(Replace Conditional with Polymorphism)
当多个类有相同行为但不同实现时,使用多态。
重构前:
class Bird {
constructor(type) {
this.type = type;
}
getSpeed() {
switch (this.type) {
case 'EUROPEAN':
return 10;
case 'AFRICAN':
return 20;
case 'NORWEGIAN_BLUE':
return 30;
default:
return 0;
}
}
getPlumage() {
switch (this.type) {
case 'EUROPEAN':
return 'average';
case 'AFRICAN':
return 'afraid';
case 'NORWEGIAN_BLUE':
return 'beautiful';
default:
return 'unknown';
}
}
}
重构后:
class Bird {
getSpeed() {
return 0;
}
getPlumage() {
return 'unknown';
}
}
class EuropeanBird extends Bird {
getSpeed() {
return 10;
}
getPlumage() {
return 'average';
}
}
class AfricanBird extends Bird {
getSpeed() {
return 20;
}
getPlumage() {
return 'afraid';
}
}
class NorwegianBlueBird extends Bird {
getSpeed() {
return 10;
}
getPlumage() {
return 'beautiful';
}
}
// 工厂函数
function createBird(type) {
switch (type) {
case 'EUROPEAN':
return new EuropeanBird();
case 'AFRICAN':
return new AfricanBird();
case 'NORWEGIAN_BLUE':
return new NorwegianBlueBird();
default:
return new Bird();
}
}
3.4 用组合替换继承(Replace Inheritance with Delegation)
当继承关系不合理时,使用组合。
重构前:
class Stack extends Array {
push(item) {
super.push(item);
return this.length;
}
pop() {
return super.pop();
}
}
重构后:
class Stack {
constructor() {
this.items = [];
}
push(item) {
this.items.push(item);
return this.items.length;
}
pop() {
return this.items.pop();
}
peek() {
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
}
第四部分:高级重构技巧
4.1 策略模式(Strategy Pattern)
将算法封装成独立的类,使其可以相互替换。
重构前:
class OrderProcessor {
process(order) {
let discount = 0;
if (order.customerType === 'VIP') {
discount = 0.1;
} else if (order.customerType === 'SVIP') {
discount = 0.2;
} else if (order.customerType === 'GOLD') {
discount = 0.05;
}
// 其他处理逻辑
const total = order.amount * (1 - discount);
// ...
}
}
重构后:
// 策略接口
class DiscountStrategy {
calculate(amount) {
throw new Error('必须实现calculate方法');
}
}
// 具体策略
class VIPDiscountStrategy extends DiscountStrategy {
calculate(amount) {
return amount * 0.9;
}
}
class SVIPDiscountStrategy extends DiscountStrategy {
calculate(amount) {
return amount * 0.8;
}
}
class GoldDiscountStrategy extends DiscountStrategy {
calculate(amount) {
return amount * 0.95;
}
}
class NoDiscountStrategy extends DiscountStrategy {
calculate(amount) {
return amount;
}
}
// 策略上下文
class OrderProcessor {
constructor(discountStrategy) {
this.discountStrategy = discountStrategy;
}
process(order) {
const total = this.discountStrategy.calculate(order.amount);
// 其他处理逻辑
return total;
}
}
// 使用
const strategies = {
'VIP': new VIPDiscountStrategy(),
'SVIP': new SVIPDiscountStrategy(),
'GOLD': new GoldDiscountStrategy(),
'NORMAL': new NoDiscountStrategy()
};
function createOrderProcessor(customerType) {
const strategy = strategies[customerType] || strategies['NORMAL'];
return new OrderProcessor(strategy);
}
4.2 观察者模式(Observer Pattern)
解耦事件的发布者和订阅者。
重构前:
class OrderSystem {
createOrder(order) {
// 创建订单
this.saveOrder(order);
// 发送邮件通知
this.sendEmail(order);
// 更新库存
this.updateInventory(order);
// 记录日志
this.logOrder(order);
}
saveOrder(order) { /* ... */ }
sendEmail(order) { /* ... */ }
updateInventory(order) { /* ... */ }
logOrder(order) { /* ... */ }
}
重构后:
// 观察者接口
class Observer {
update(order) {
throw new Error('必须实现update方法');
}
}
// 具体观察者
class EmailNotificationObserver extends Observer {
update(order) {
console.log(`发送邮件通知: ${order.id}`);
}
}
class InventoryObserver extends Observer {
update(order) {
console.log(`更新库存: ${order.id}`);
}
}
class LogObserver extends Observer {
update(order) {
console.log(`记录日志: ${order.id}`);
}
}
// 主题(发布者)
class OrderSystem {
constructor() {
this.observers = [];
}
attach(observer) {
this.observers.push(observer);
}
detach(observer) {
const index = this.observers.indexOf(observer);
if (index > -1) {
this.observers.splice(index, 1);
}
}
notify(order) {
this.observers.forEach(observer => observer.update(order));
}
createOrder(order) {
// 创建订单
this.saveOrder(order);
// 通知所有观察者
this.notify(order);
}
saveOrder(order) { /* ... */ }
}
4.3 依赖注入(Dependency Injection)
将依赖关系从内部创建改为外部传入。
重构前:
class UserService {
constructor() {
this.database = new Database();
this.logger = new Logger();
this.emailService = new EmailService();
}
createUser(userData) {
this.logger.log('Creating user');
this.database.save('users', userData);
this.emailService.sendWelcomeEmail(userData.email);
}
}
重构后:
class UserService {
constructor(database, logger, emailService) {
this.database = database;
this.logger = logger;
this.emailService = emailService;
}
createUser(userData) {
this.logger.log('Creating user');
this.database.save('users', userData);
this.emailService.sendWelcomeEmail(userData.email);
}
}
// 使用
const database = new Database();
const logger = new Logger();
const emailService = new EmailService();
const userService = new UserService(database, logger, emailService);
4.4 用卫语句替换嵌套条件(Replace Nested Conditional with Guard Clauses)
将深层嵌套的条件判断提前返回。
重构前:
function getDiscount(order) {
let discount = 0;
if (order != null) {
if (order.customer != null) {
if (order.customer.type === 'VIP') {
discount = 0.1;
} else if (order.customer.type === 'SVIP') {
discount = 0.2;
}
}
}
return discount;
}
重构后:
function getDiscount(order) {
if (!order || !order.customer) {
return 0;
}
if (order.customer.type === 'VIP') {
return 0.1;
}
if (order.customer.type === 'SVIP') {
return 0.2;
}
return 0;
}
第五部分:常见问题与解决方案
5.1 问题:代码重复
症状:相同的逻辑在多个地方出现。
解决方案:提取公共函数或使用模板方法模式。
示例:
// 重构前:多处重复的验证逻辑
function validateUser(user) {
if (!user.name || user.name.length < 2) {
return false;
}
if (!user.email || !user.email.includes('@')) {
return false;
}
return true;
}
function validateProduct(product) {
if (!product.name || product.name.length < 2) {
return false;
}
if (!product.price || product.price <= 0) {
return false;
}
return true;
}
// 重构后:提取通用验证器
function createLengthValidator(minLength) {
return (value) => value && value.length >= minLength;
}
function createRequiredValidator() {
return (value) => value != null && value !== '';
}
function createPatternValidator(pattern) {
return (value) => pattern.test(value);
}
function createRangeValidator(min, max) {
return (value) => value >= min && value <= max;
}
function validate(data, rules) {
for (const [field, validators] of Object.entries(rules)) {
for (const validator of validators) {
if (!validator(data[field])) {
return false;
}
}
}
return true;
}
// 使用
const userRules = {
name: [createRequiredValidator(), createLengthValidator(2)],
email: [createRequiredValidator(), createPatternValidator(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)]
};
const productRules = {
name: [createRequiredValidator(), createLengthValidator(2)],
price: [createRequiredValidator(), createRangeValidator(0.01, 1000000)]
};
validate(user, userRules);
validate(product, productRules);
5.2 问题:过长的条件链
症状:大量的if-else if或switch语句。
解决方案:使用策略模式或映射对象。
示例:
// 重构前
function getShippingCost(country, weight) {
if (country === 'US') {
return weight * 5;
} else if (country === 'CA') {
return weight * 6;
} else if (country === 'UK') {
return weight * 7;
} else if (country === 'DE') {
return weight * 8;
} else {
return weight * 10;
}
}
// 重构后
const shippingRates = {
'US': 5,
'CA': 6,
'UK': 7,
'DE': 8
};
function getShippingCost(country, weight) {
const rate = shippingRates[country] || 10;
return weight * rate;
}
5.3 问题:上帝类(God Class)
症状:一个类有上千行代码,承担过多职责。
解决方案:按职责拆分为多个类。
示例:
// 重构前:上帝类
class ECommerceSystem {
// 用户管理
createUser() { /* ... */ }
updateUser() { /* ... */ }
deleteUser() { /* ... */ }
// 订单管理
createOrder() { /* ... */ }
cancelOrder() { /* ... */ }
getOrderStatus() { /* ... */ }
// 支付处理
processPayment() { /* ... */ }
refundPayment() { /* ... */ }
// 库存管理
updateInventory() { /* ... */ }
checkStock() { /* ... */ }
// 报表生成
generateSalesReport() { /* ... */ }
generateUserReport() { /* ... */ }
}
// 重构后:职责分离
class UserManager {
createUser() { /* ... */ }
updateUser() { /* ... */ }
deleteUser() { /* ... */ }
}
class OrderManager {
createOrder() { /* ... */ }
cancelOrder() { /* ... */ }
getOrderStatus() { /* ... */ }
}
class PaymentProcessor {
processPayment() { /* ... */ }
refundPayment() { /* ... */ }
}
class InventoryManager {
updateInventory() { /* ... */ }
checkStock() { /* ... */ }
}
class ReportGenerator {
generateSalesReport() { /* ... */ }
generateUserReport() { /* ... */ }
}
class ECommerceSystem {
constructor() {
this.userManager = new UserManager();
this.orderManager = new OrderManager();
this.paymentProcessor = new PaymentProcessor();
this.inventoryManager = new InventoryManager();
this.reportGenerator = new ReportGenerator();
}
}
5.4 问题:过度使用回调(Callback Hell)
症状:多层嵌套的回调函数,难以阅读和维护。
解决方案:使用Promise或async/await。
示例:
// 重构前:回调地狱
function processUserOrder(userId, orderId, callback) {
getUser(userId, function(err, user) {
if (err) {
callback(err);
} else {
getOrder(orderId, function(err, order) {
if (err) {
callback(err);
} else {
getPayment(order.paymentId, function(err, payment) {
if (err) {
callback(err);
} else {
sendEmail(user.email, order, payment, function(err, result) {
if (err) {
callback(err);
} else {
callback(null, { user, order, payment, result });
}
});
}
});
}
});
}
});
}
// 重构后:使用Promise
function getUserAsync(userId) {
return new Promise((resolve, reject) => {
getUser(userId, (err, user) => {
if (err) reject(err);
else resolve(user);
});
});
}
function getOrderAsync(orderId) {
return new Promise((resolve, reject) => {
getOrder(orderId, (err, order) => {
if (err) reject(err);
else resolve(order);
});
});
}
function getPaymentAsync(paymentId) {
return new Promise((resolve, reject) => {
getPayment(paymentId, (err, payment) => {
if (err) reject(err);
else resolve(payment);
});
});
}
function sendEmailAsync(email, order, payment) {
return new Promise((resolve, reject) => {
sendEmail(email, order, payment, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
async function processUserOrder(userId, orderId) {
try {
const user = await getUserAsync(userId);
const order = await getOrderAsync(orderId);
const payment = await getPaymentAsync(order.paymentId);
const result = await sendEmailAsync(user.email, order, payment);
return { user, order, payment, result };
} catch (error) {
throw error;
}
}
5.5 问题:全局状态滥用
症状:代码中大量使用全局变量,难以追踪状态变化。
解决方案:使用状态管理器或封装状态。
示例:
// 重构前:滥用全局变量
let currentUser = null;
let cartItems = [];
let isLoading = false;
function login(username, password) {
isLoading = true;
// 登录逻辑
currentUser = { username, id: 123 };
isLoading = false;
}
function addToCart(item) {
cartItems.push(item);
}
function checkout() {
if (!currentUser) {
alert('请先登录');
return;
}
// 结账逻辑
cartItems = [];
}
// 重构后:封装状态
class AppState {
constructor() {
this._currentUser = null;
this._cartItems = [];
this._isLoading = false;
this._listeners = [];
}
// Getter
get currentUser() {
return this._currentUser;
}
get cartItems() {
return [...this._cartItems]; // 返回副本,防止外部修改
}
get isLoading() {
return this._isLoading;
}
// Setter(私有)
_setCurrentUser(user) {
this._currentUser = user;
this._notify();
}
_setCartItems(items) {
this._cartItems = items;
this._notify();
}
_setIsLoading(loading) {
this._isLoading = loading;
this._notify();
}
// 订阅变化
subscribe(listener) {
this._listeners.push(listener);
return () => {
this._listeners = this._listeners.filter(l => l !== listener);
};
}
_notify() {
this._listeners.forEach(listener => listener(this));
}
// 业务方法
async login(username, password) {
this._setIsLoading(true);
try {
// 模拟API调用
const user = await api.login(username, password);
this._setCurrentUser(user);
} finally {
this._setIsLoading(false);
}
}
addToCart(item) {
this._setCartItems([...this._cartItems, item]);
}
checkout() {
if (!this._currentUser) {
throw new Error('请先登录');
}
// 结账逻辑
this._setCartItems([]);
}
}
// 使用
const appState = new AppState();
// UI组件订阅状态变化
const unsubscribe = appState.subscribe((state) => {
renderUI(state);
});
// 业务逻辑
await appState.login('username', 'password');
appState.addToCart({ id: 1, name: '商品' });
appState.checkout();
// 清理
unsubscribe();
第六部分:重构最佳实践
6.1 重构工作流
标准重构流程:
- 编写测试:确保有可靠的测试覆盖
- 小步重构:每次只做一个小的改动
- 频繁测试:每次改动后运行测试
- 代码审查:团队成员审查重构结果
- 文档更新:更新相关文档和注释
6.2 重构与版本控制
最佳实践:
- 为每个重构步骤创建单独的提交
- 使用有意义的提交信息(如”Extract validateOrder function”)
- 重构完成后合并提交,保持主分支整洁
- 使用分支进行大型重构
示例提交信息:
git commit -m "refactor: extract order validation logic into separate function"
git commit -m "refactor: replace magic numbers with named constants"
git commit -m "refactor: extract discount calculation to strategy pattern"
6.3 重构与代码审查
审查清单:
- [ ] 重构是否保持了原有功能?
- [ ] 新的代码结构是否更清晰?
- [ ] 是否有更好的命名?
- [ ] 是否引入了不必要的复杂性?
- [ ] 测试覆盖率是否足够?
- [ ] 文档是否需要更新?
6.4 重构与性能
注意事项:
- 重构主要关注代码结构,不是性能优化
- 性能优化应该在重构后进行
- 使用性能分析工具识别瓶颈
- 避免过早优化
6.5 团队重构实践
团队协作建议:
- 结对重构:两人一组,一人操作一人审查
- 重构会议:定期讨论重构计划和进展
- 知识共享:分享重构技巧和经验
- 代码规范:制定团队重构规范
第七部分:重构工具与资源
7.1 IDE重构工具
VS Code:
- F2:重命名符号
- Shift+Alt+O:提取函数
- Shift+Alt+I:内联函数
- Shift+Alt+R:重构菜单
IntelliJ IDEA/WebStorm:
- Ctrl+Alt+M:提取方法
- Ctrl+Alt+V:提取变量
- Ctrl+Alt+P:提取参数
- Ctrl+Alt+N:内联
7.2 静态分析工具
JavaScript/TypeScript:
- ESLint:代码规范检查
- Prettier:代码格式化
- SonarQube:代码质量分析
- TypeScript:类型检查
Python:
- pylint:代码规范检查
- black:代码格式化
- mypy:类型检查
Java:
- Checkstyle:代码规范
- PMD:静态分析
- SpotBugs:bug检测
7.3 自动化测试框架
JavaScript:
- Jest:测试框架
- Mocha + Chai:测试组合
- Cypress:端到端测试
Python:
- pytest:测试框架
- unittest:标准库测试
Java:
- JUnit:测试框架
- TestNG:高级测试框架
7.4 学习资源
书籍:
- 《重构:改善既有代码的设计》(Martin Fowler)
- 《代码整洁之道》(Robert C. Martin)
- 《设计模式:可复用面向对象软件的基础》(GoF)
在线资源:
- Refactoring.Guru:重构指南和模式
- Martin Fowler’s Bliki:技术博客
- Stack Overflow:问题解答
视频课程:
- Udemy:代码重构课程
- Pluralsight:软件质量课程
- Coursera:软件工程课程
第八部分:实战案例分析
8.1 案例:电商系统订单处理模块重构
背景:一个电商系统的订单处理模块,代码混乱,难以维护。
重构前代码:
// 一个巨大的函数处理所有订单逻辑
function processOrder(orderData) {
// 1. 验证数据
if (!orderData.userId) {
throw new Error('缺少用户ID');
}
if (!orderData.items || orderData.items.length === 0) {
throw new Error('缺少商品项');
}
// 2. 计算总价
let total = 0;
for (let i = 0; i < orderData.items.length; i++) {
const item = orderData.items[i];
total += item.price * item.quantity;
}
// 3. 应用折扣
if (orderData.customerType === 'VIP') {
total = total * 0.9;
} else if (orderData.customerType === 'SVIP') {
total = total * 0.8;
} else if (orderData.customerType === 'GOLD') {
total = total * 0.95;
}
// 4. 计算运费
let shippingCost = 0;
if (orderData.country === 'US') {
shippingCost = 10;
} else if (orderData.country === 'CA') {
shippingCost = 15;
} else if (orderData.country === 'UK') {
shippingCost = 20;
} else {
shippingCost = 25;
}
// 5. 计算税费
const tax = total * 0.1;
// 6. 最终总价
const finalTotal = total + shippingCost + tax;
// 7. 验证库存
for (const item of orderData.items) {
const stock = db.getStock(item.id);
if (stock < item.quantity) {
throw new Error(`商品 ${item.name} 库存不足`);
}
}
// 8. 扣减库存
for (const item of orderData.items) {
db.updateStock(item.id, -item.quantity);
}
// 9. 创建订单记录
const orderId = db.insert('orders', {
userId: orderData.userId,
items: orderData.items,
total: finalTotal,
status: 'pending'
});
// 10. 发送邮件
const user = db.getUser(orderData.userId);
emailService.send(user.email, '订单确认', `您的订单 ${orderId} 已创建,总金额:${finalTotal}`);
// 11. 记录日志
logger.info(`订单创建成功: ${orderId}, 用户: ${orderData.userId}, 金额: ${finalTotal}`);
return {
orderId,
total: finalTotal,
status: 'pending'
};
}
重构步骤:
步骤1:提取验证逻辑
class OrderValidator {
validate(orderData) {
this.validateRequiredFields(orderData);
this.validateItems(orderData.items);
}
validateRequiredFields(orderData) {
if (!orderData.userId) {
throw new Error('缺少用户ID');
}
if (!orderData.items || orderData.items.length === 0) {
throw new Error('缺少商品项');
}
}
validateItems(items) {
for (const item of items) {
if (!item.id || !item.price || !item.quantity) {
throw new Error(`商品 ${item.name || ''} 信息不完整`);
}
}
}
}
步骤2:提取价格计算逻辑
class PriceCalculator {
calculateSubtotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
applyDiscount(subtotal, customerType) {
const discountMap = {
'VIP': 0.9,
'SVIP': 0.8,
'GOLD': 0.95
};
const discount = discountMap[customerType] || 1;
return subtotal * discount;
}
calculateShippingCost(country) {
const shippingRates = {
'US': 10,
'CA': 15,
'UK': 20
};
return shippingRates[country] || 25;
}
calculateTax(amount) {
return amount * 0.1;
}
calculateTotal(items, customerType, country) {
const subtotal = this.calculateSubtotal(items);
const discounted = this.applyDiscount(subtotal, customerType);
const shipping = this.calculateShippingCost(country);
const tax = this.calculateTax(discounted);
return {
subtotal,
discounted,
shipping,
tax,
total: discounted + shipping + tax
};
}
}
步骤3:提取库存管理逻辑
class InventoryManager {
checkStock(items) {
for (const item of items) {
const stock = db.getStock(item.id);
if (stock < item.quantity) {
throw new Error(`商品 ${item.name} 库存不足,当前库存:${stock}`);
}
}
}
reserveStock(items) {
for (const item of items) {
db.updateStock(item.id, -item.quantity);
}
}
releaseStock(items) {
for (const item of items) {
db.updateStock(item.id, item.quantity);
}
}
}
步骤4:提取通知服务
class NotificationService {
constructor(emailService, logger) {
this.emailService = emailService;
this.logger = logger;
}
async sendOrderConfirmation(user, order) {
const subject = '订单确认';
const content = `您的订单 ${order.id} 已创建,总金额:${order.total}`;
await this.emailService.send(user.email, subject, content);
}
logOrderCreation(order) {
this.logger.info(`订单创建成功: ${order.id}, 用户: ${order.userId}, 金额: ${order.total}`);
}
}
步骤5:创建订单服务(外观模式)
class OrderService {
constructor() {
this.validator = new OrderValidator();
this.priceCalculator = new PriceCalculator();
this.inventoryManager = new InventoryManager();
this.notificationService = new NotificationService(
new EmailService(),
new Logger()
);
}
async createOrder(orderData) {
// 1. 验证
this.validator.validate(orderData);
// 2. 检查库存
this.inventoryManager.checkStock(orderData.items);
// 3. 计算价格
const priceBreakdown = this.priceCalculator.calculateTotal(
orderData.items,
orderData.customerType,
orderData.country
);
// 4. 扣减库存
this.inventoryManager.reserveStock(orderData.items);
try {
// 5. 创建订单
const orderId = db.insert('orders', {
userId: orderData.userId,
items: orderData.items,
...priceBreakdown,
status: 'pending'
});
// 6. 获取用户信息
const user = db.getUser(orderData.userId);
// 7. 发送通知
await this.notificationService.sendOrderConfirmation(user, {
id: orderId,
total: priceBreakdown.total
});
// 8. 记录日志
this.notificationService.logOrderCreation({
id: orderId,
userId: orderData.userId,
total: priceBreakdown.total
});
return {
orderId,
...priceBreakdown,
status: 'pending'
};
} catch (error) {
// 如果订单创建失败,释放库存
this.inventoryManager.releaseStock(orderData.items);
throw error;
}
}
}
重构后使用:
const orderService = new OrderService();
try {
const result = await orderService.createOrder({
userId: 'U001',
customerType: 'VIP',
country: 'US',
items: [
{ id: 'P001', name: '商品A', price: 100, quantity: 2 },
{ id: 'P002', name: '商品B', price: 200, quantity: 1 }
]
});
console.log('订单创建成功:', result);
} catch (error) {
console.error('订单创建失败:', error.message);
}
重构收益:
- 代码行数从100+减少到50+(不包括类定义)
- 每个类职责单一,易于测试
- 可以独立修改价格计算逻辑
- 错误处理更加健壮
- 代码可读性大幅提升
8.2 案例:前端组件重构
重构前:
// 一个复杂的React组件,承担过多职责
class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = {
products: [],
filteredProducts: [],
searchQuery: '',
sortBy: 'name',
sortOrder: 'asc',
selectedCategory: 'all',
loading: false,
error: null,
cart: [],
showCart: false
};
}
componentDidMount() {
this.fetchProducts();
}
fetchProducts = async () => {
this.setState({ loading: true });
try {
const response = await fetch('/api/products');
const products = await response.json();
this.setState({ products, filteredProducts: products, loading: false });
} catch (error) {
this.setState({ error: error.message, loading: false });
}
}
handleSearch = (e) => {
const searchQuery = e.target.value;
this.setState({ searchQuery }, () => this.filterProducts());
}
handleSort = (field) => {
const { sortBy, sortOrder } = this.state;
const newSortOrder = sortBy === field && sortOrder === 'asc' ? 'desc' : 'asc';
this.setState({ sortBy: field, sortOrder: newSortOrder }, () => this.sortProducts());
}
handleCategoryChange = (category) => {
this.setState({ selectedCategory: category }, () => this.filterProducts());
}
filterProducts = () => {
const { products, searchQuery, selectedCategory } = this.state;
let filtered = products;
if (searchQuery) {
filtered = filtered.filter(p =>
p.name.toLowerCase().includes(searchQuery.toLowerCase())
);
}
if (selectedCategory !== 'all') {
filtered = filtered.filter(p => p.category === selectedCategory);
}
this.setState({ filteredProducts: filtered }, () => this.sortProducts());
}
sortProducts = () => {
const { filteredProducts, sortBy, sortOrder } = this.state;
const sorted = [...filteredProducts].sort((a, b) => {
if (a[sortBy] < b[sortBy]) return sortOrder === 'asc' ? -1 : 1;
if (a[sortBy] > b[sortBy]) return sortOrder === 'asc' ? 1 : -1;
return 0;
});
this.setState({ filteredProducts: sorted });
}
addToCart = (product) => {
this.setState(prevState => ({
cart: [...prevState.cart, product]
}));
}
removeFromCart = (productId) => {
this.setState(prevState => ({
cart: prevState.cart.filter(item => item.id !== productId)
}));
}
toggleCart = () => {
this.setState(prevState => ({ showCart: !prevState.showCart }));
}
render() {
const { filteredProducts, loading, error, cart, showCart } = this.state;
if (loading) return <div>加载中...</div>;
if (error) return <div>错误: {error}</div>;
return (
<div>
<div>
<input
type="text"
placeholder="搜索商品"
onChange={this.handleSearch}
/>
<select onChange={(e) => this.handleCategoryChange(e.target.value)}>
<option value="all">全部</option>
<option value="electronics">电子产品</option>
<option value="clothing">服装</option>
</select>
<button onClick={() => this.handleSort('name')}>按名称排序</button>
<button onClick={() => this.handleSort('price')}>按价格排序</button>
<button onClick={this.toggleCart}>
购物车 ({cart.length})
</button>
</div>
<div>
{filteredProducts.map(product => (
<div key={product.id}>
<h3>{product.name}</h3>
<p>价格: {product.price}</p>
<button onClick={() => this.addToCart(product)}>加入购物车</button>
</div>
))}
</div>
{showCart && (
<div>
<h2>购物车</h2>
{cart.map((item, index) => (
<div key={index}>
{item.name} - {item.price}
<button onClick={() => this.removeFromCart(item.id)}>删除</button>
</div>
))}
</div>
)}
</div>
);
}
}
重构后:
1. 提取自定义Hook(useProducts)
// hooks/useProducts.js
import { useState, useEffect, useCallback } from 'react';
export const useProducts = () => {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchProducts = useCallback(async () => {
setLoading(true);
try {
const response = await fetch('/api/products');
const data = await response.json();
setProducts(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchProducts();
}, [fetchProducts]);
return { products, loading, error, refetch: fetchProducts };
};
2. 提取自定义Hook(useFilters)
// hooks/useFilters.js
import { useState, useMemo } from 'react';
export const useFilters = (products) => {
const [searchQuery, setSearchQuery] = useState('');
const [selectedCategory, setSelectedCategory] = useState('all');
const [sortBy, setSortBy] = useState('name');
const [sortOrder, setSortOrder] = useState('asc');
const filteredProducts = useMemo(() => {
let result = [...products];
// 搜索过滤
if (searchQuery) {
result = result.filter(p =>
p.name.toLowerCase().includes(searchQuery.toLowerCase())
);
}
// 分类过滤
if (selectedCategory !== 'all') {
result = result.filter(p => p.category === selectedCategory);
}
// 排序
result.sort((a, b) => {
if (a[sortBy] < b[sortBy]) return sortOrder === 'asc' ? -1 : 1;
if (a[sortBy] > b[sortBy]) return sortOrder === 'asc' ? 1 : -1;
return 0;
});
return result;
}, [products, searchQuery, selectedCategory, sortBy, sortOrder]);
return {
filteredProducts,
searchQuery,
setSearchQuery,
selectedCategory,
setSelectedCategory,
sortBy,
setSortBy,
sortOrder,
setSortOrder
};
};
3. 提取自定义Hook(useCart)
// hooks/useCart.js
import { useState } from 'react';
export const useCart = () => {
const [cart, setCart] = useState([]);
const [showCart, setShowCart] = useState(false);
const addToCart = (product) => {
setCart(prev => [...prev, product]);
};
const removeFromCart = (productId) => {
setCart(prev => prev.filter(item => item.id !== productId));
};
const toggleCart = () => {
setShowCart(prev => !prev);
};
const clearCart = () => {
setCart([]);
};
return {
cart,
showCart,
addToCart,
removeFromCart,
toggleCart,
clearCart
};
};
4. 提取UI组件
// components/ProductFilters.jsx
export const ProductFilters = ({
searchQuery,
onSearchChange,
selectedCategory,
onCategoryChange,
onSort,
cartLength,
onToggleCart
}) => {
return (
<div className="filters">
<input
type="text"
placeholder="搜索商品"
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
/>
<select value={selectedCategory} onChange={(e) => onCategoryChange(e.target.value)}>
<option value="all">全部</option>
<option value="electronics">电子产品</option>
<option value="clothing">服装</option>
</select>
<button onClick={() => onSort('name')}>按名称排序</button>
<button onClick={() => onSort('price')}>按价格排序</button>
<button onClick={onToggleCart}>
购物车 ({cartLength})
</button>
</div>
);
};
// components/ProductItem.jsx
export const ProductItem = ({ product, onAddToCart }) => {
return (
<div className="product-item">
<h3>{product.name}</h3>
<p>价格: {product.price}</p>
<button onClick={() => onAddToCart(product)}>加入购物车</button>
</div>
);
};
// components/Cart.jsx
export const Cart = ({ items, onRemoveFromCart }) => {
if (items.length === 0) {
return <div>购物车为空</div>;
}
return (
<div className="cart">
<h2>购物车</h2>
{items.map((item, index) => (
<div key={index} className="cart-item">
{item.name} - {item.price}
<button onClick={() => onRemoveFromCart(item.id)}>删除</button>
</div>
))}
</div>
);
};
5. 重构后的主组件
// ProductList.jsx
import React from 'react';
import { useProducts } from './hooks/useProducts';
import { useFilters } from './hooks/useFilters';
import { useCart } from './hooks/useCart';
import { ProductFilters } from './components/ProductFilters';
import { ProductItem } from './components/ProductItem';
import { Cart } from './components/Cart';
const ProductList = () => {
// 使用自定义Hook分离关注点
const { products, loading, error } = useProducts();
const {
filteredProducts,
searchQuery,
setSearchQuery,
selectedCategory,
setSelectedCategory,
sortBy,
setSortBy,
sortOrder,
setSortOrder
} = useFilters(products);
const {
cart,
showCart,
addToCart,
removeFromCart,
toggleCart
} = useCart();
// 处理排序
const handleSort = (field) => {
if (sortBy === field) {
setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
} else {
setSortBy(field);
setSortOrder('asc');
}
};
if (loading) return <div>加载中...</div>;
if (error) return <div>错误: {error}</div>;
return (
<div className="product-list">
<ProductFilters
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
selectedCategory={selectedCategory}
onCategoryChange={setSelectedCategory}
onSort={handleSort}
cartLength={cart.length}
onToggleCart={toggleCart}
/>
<div className="product-grid">
{filteredProducts.map(product => (
<ProductItem
key={product.id}
product={product}
onAddToCart={addToCart}
/>
))}
</div>
{showCart && (
<Cart
items={cart}
onRemoveFromCart={removeFromCart}
/>
)}
</div>
);
};
export default ProductList;
重构收益:
- 代码从200+行减少到80+行
- 每个Hook职责单一,可独立测试
- UI组件可复用
- 状态管理清晰,易于维护
- 性能优化(useMemo避免不必要的计算)
第九部分:重构检查清单
9.1 重构前检查清单
- [ ] 是否理解当前代码的业务逻辑?
- [ ] 是否有完整的测试覆盖?
- [ ] 是否备份了当前代码?
- [ ] 是否制定了重构计划?
- [ ] 是否选择了合适的重构时机?
9.2 重构中检查清单
- [ ] 是否保持了原有功能?
- [ ] 是否每次改动都运行了测试?
- [ ] 是否使用了有意义的命名?
- [ ] 是否减少了代码重复?
- [ ] 是否降低了复杂度?
- [ ] 是否提高了可读性?
9.3 重构后检查清单
- [ ] 所有测试是否通过?
- [ ] 代码审查是否完成?
- [ ] 文档是否更新?
- [ ] 性能是否可接受?
- [ ] 是否有更好的命名或结构?
- [ ] 是否可以进一步简化?
第十部分:总结与建议
10.1 重构的核心原则总结
- 保持行为不变:这是重构的底线
- 小步前进:每次只做一点改动
- 测试驱动:没有测试就不要重构
- 持续进行:重构是日常工作,不是一次性任务
- 团队协作:重构需要团队共识
10.2 重构的常见误区
误区1:重构就是重写
- 正解:重构是渐进式改进,不是推倒重来
误区2:重构会引入bug
- 正解:有测试保护的重构是安全的
误区3:重构会浪费时间
- 正解:重构节省长期维护成本
误区4:只有老代码需要重构
- 正解:新代码也需要持续重构
10.3 重构能力的提升路径
初级阶段:
- 掌握基础重构技巧
- 学会识别代码坏味道
- 能够完成简单的提取和内联
中级阶段:
- 熟练运用设计模式
- 能够重构复杂业务逻辑
- 具备测试编写能力
高级阶段:
- 能够设计重构策略
- 领导团队重构实践
- 建立重构规范和文化
10.4 持续改进的建议
- 建立重构文化:让重构成为团队习惯
- 定期重构:安排专门的重构时间
- 代码审查:将重构纳入审查标准
- 知识分享:定期分享重构经验
- 工具支持:充分利用现代IDE工具
- 度量改进:跟踪代码质量指标
10.5 最后的建议
重构是一项需要持续学习和实践的技能。建议:
- 从简单开始:先从提取函数、重命名等简单技巧入手
- 阅读经典:深入学习《重构》等经典著作
- 实践为主:在实际项目中不断练习
- 保持耐心:重构能力需要时间积累
- 享受过程:看到代码变得优雅会带来巨大满足感
记住,好的代码不是一蹴而就的,而是通过持续重构和改进得来的。每一次小的重构都是对代码质量的投资,最终会带来巨大的回报。
