系统重构:这次怎么落地的
这篇是系统重构的实操备忘,偏决策和限制条件。
技术债务评估
代码复杂度
# 使用 ESLint 检查代码复杂度
npx eslint --ext .js,.jsx src/ --format json > complexity-report.json
# 使用 SonarQube
sonar-scanner \
-Dsonar.projectKey=myproject \
-Dsonar.sources=src \
-Dsonar.host.url=https://sonarqube.example.com
技术债务比率
// 计算技术债务比率
function calculateTechnicalDebt() {
const totalLines = countTotalLines();
const debtLines = countTechnicalDebtLines();
return (debtLines / totalLines) * 100;
}
// 债务比率解释
// 0-5%: 良好
// 5-10%: 可以接受
// 10-20%: 需要关注
// >20%: 需要重构
代码重复率
# 使用 JSCPD 检测代码重复
npx jscpd src/ --format json > duplication-report.json
# 结果示例
{
"statistics": {
"totalLines": 10000,
"duplicateLines": 1000,
"percentage": 10
}
}
重构策略
小步重构
// 重构前
function processUser(user) {
if (user.age < 18) {
return 'minor';
} else if (user.age >= 18 && user.age < 65) {
return 'adult';
} else {
return 'senior';
}
}
// 重构:提取常量
const AGE_MINOR = 18;
const AGE_SENIOR = 65;
function processUser(user) {
if (user.age < AGE_MINOR) {
return 'minor';
} else if (user.age < AGE_SENIOR) {
return 'adult';
} else {
return 'senior';
}
}
// 重构:提取方法
function getUserAgeGroup(age) {
if (age < AGE_MINOR) return 'minor';
if (age < AGE_SENIOR) return 'adult';
return 'senior';
}
提取函数
// 重构前
function calculateOrderTotal(order) {
let total = 0;
for (let item of order.items) {
if (item.discount) {
total += item.price * item.quantity * (1 - item.discount);
} else {
total += item.price * item.quantity;
}
}
if (order.coupon) {
total = total * (1 - order.coupon.discount);
}
return total;
}
// 重构:提取函数
function calculateItemPrice(item) {
const price = item.price * item.quantity;
return item.discount ? price * (1 - item.discount) : price;
}
function applyCouponDiscount(total, coupon) {
return coupon ? total * (1 - coupon.discount) : total;
}
function calculateOrderTotal(order) {
const itemsTotal = order.items.reduce((sum, item) =>
sum + calculateItemPrice(item), 0
);
return applyCouponDiscount(itemsTotal, order.coupon);
}
提取类
// 重构前
function validateUser(user) {
if (!user.email || !isValidEmail(user.email)) {
throw new Error('Invalid email');
}
if (!user.password || user.password.length < 8) {
throw new Error('Password too short');
}
if (!user.age || user.age < 18) {
throw new Error('User must be 18 or older');
}
}
// 重构:提取类
class UserValidator {
validateEmail(email) {
if (!email || !isValidEmail(email)) {
throw new Error('Invalid email');
}
}
validatePassword(password) {
if (!password || password.length < 8) {
throw new Error('Password too short');
}
}
validateAge(age) {
if (!age || age < 18) {
throw new Error('User must be 18 or older');
}
}
validate(user) {
this.validateEmail(user.email);
this.validatePassword(user.password);
this.validateAge(user.age);
}
}
const validator = new UserValidator();
validator.validate(user);
架构重构
分层架构
// 重构前:所有逻辑都在一个文件
app.post('/api/users', async (req, res) => {
// 验证逻辑
if (!req.body.email) {
return res.status(400).json({ error: 'Email required' });
}
// 业务逻辑
const user = await db.query('SELECT * FROM users WHERE email = ?', [req.body.email]);
if (user) {
return res.status(400).json({ error: 'User already exists' });
}
const newUser = await db.query('INSERT INTO users SET ?', req.body);
// 响应逻辑
res.json(newUser);
});
// 重构:分层架构
// Controller 层
class UserController {
constructor(userService) {
this.userService = userService;
}
async create(req, res) {
try {
const user = await this.userService.create(req.body);
res.json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
}
}
// Service 层
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async create(userData) {
await this.validateEmail(userData.email);
const existingUser = await this.userRepository.findByEmail(userData.email);
if (existingUser) {
throw new Error('User already exists');
}
return this.userRepository.create(userData);
}
async validateEmail(email) {
if (!email) {
throw new Error('Email required');
}
}
}
// Repository 层
class UserRepository {
constructor(db) {
this.db = db;
}
async findByEmail(email) {
return this.db.query('SELECT * FROM users WHERE email = ?', [email]);
}
async create(userData) {
return this.db.query('INSERT INTO users SET ?', userData);
}
}
依赖注入
// 重构前:直接依赖
class OrderService {
async create(orderData) {
const db = require('./db');
const paymentService = require('./payment-service');
const order = await db.query('INSERT INTO orders SET ?', orderData);
await paymentService.processPayment(order);
return order;
}
}
// 重构:依赖注入
class OrderService {
constructor(db, paymentService) {
this.db = db;
this.paymentService = paymentService;
}
async create(orderData) {
const order = await this.db.query('INSERT INTO orders SET ?', orderData);
await this.paymentService.processPayment(order);
return order;
}
}
// 使用
const db = new Database();
const paymentService = new PaymentService();
const orderService = new OrderService(db, paymentService);
重构工具
重构检查清单
## 重构检查清单
### 重构前
- [ ] 有足够的测试覆盖
- [ ] 理解现有代码
- [ ] 识别重构范围
- [ ] 制定重构计划
### 重构中
- [ ] 小步重构
- [ ] 每步都有测试
- [ ] 保持代码可编译
- [ ] 及时提交代码
### 重构后
- [ ] 所有测试通过
- [ ] 代码审查完成
- [ ] 性能测试通过
- [ ] 文档更新
自动化重构
// 使用 Jest Snapshot 测试
describe('UserFormatter', () => {
test('should format user correctly', () => {
const user = { name: 'Alice', age: 30, email: '[email protected]' };
const formatted = formatUser(user);
expect(formatted).toMatchSnapshot();
});
});
// 使用 Prettier 自动格式化
npx prettier --write src/**/*.js
// 使用 ESLint 自动修复
npx eslint --fix src/**/*.js
踩过的坑
坑一:重构范围太大
一次重构太多,导致难以回滚。
解决:小步重构,每次只改一个功能。
// 错误:一次重构太多
function refactorEverything() {
refactorDataAccess();
refactorBusinessLogic();
refactorValidation();
refactorLogging();
}
// 正确:小步重构
function refactorDataAccess() {
// 只重构数据访问层
}
function refactorBusinessLogic() {
// 下一步重构业务逻辑层
}
坑二:没有测试保护
重构破坏了现有功能,没有测试保护。
解决:重构前先写测试。
// 重构前:先写测试
describe('calculateOrderTotal', () => {
test('should calculate total correctly', () => {
const order = {
items: [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 1 }
]
};
expect(calculateOrderTotal(order)).toBe(25);
});
});
// 然后重构
function calculateOrderTotal(order) {
// 新的实现
}
坑三:过度重构
为了重构而重构,没有实际价值。
解决:只重构有价值的地方。
// 不必要的重构:只是改个变量名
// 旧代码
function processData(data) {
return data.map(x => x * 2);
}
// 新代码
function transformData(data) {
return data.map(item => item * 2);
}
// 如果旧代码已经清晰,这样的重构没有价值
写在最后
重构这东西,不是技术问题,是投资回报问题。
解决了:
- 维护成本降低
- 开发效率提升
- 代码质量提高
带来了:
- 短期成本
- 学习成本
- 风险
重构之前先评估:
- 技术债务
- 投资回报
- 团队能力
- 时间预算
不是所有代码都需要重构,有时候重写更简单。
这次系统重构花了一个月,从技术债务评估到重构策略。重构完成后,开发效率提升了 50%,Bug 数量减少了 40%。
版权声明: 本文首发于 指尖魔法屋-系统重构:这次怎么落地的(https://blog.thinkmoon.cn/post/81-system-refactoring-technical-debt-strategy-practice/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。