Code Refactoring
Refactoring Principles
When to Refactor
- - Before adding new features (make change easy, then make easy change)
- After getting tests passing (red-green-refactor)
- When you see code smells
- During code review feedback
When NOT to Refactor
- - Without tests covering the code
- Under tight deadlines with no safety net
- Code that will be replaced soon
- When you don't understand what the code does
Common Code Smells
Long Methods
CODEBLOCK0
Deeply Nested Conditionals
CODEBLOCK1
Primitive Obsession
CODEBLOCK2
Feature Envy
CODEBLOCK3
Refactoring Techniques
Extract Method
CODEBLOCK4
Replace Conditional with Polymorphism
CODEBLOCK5
Introduce Parameter Object
CODEBLOCK6
Replace Magic Numbers with Constants
CODEBLOCK7
Safe Refactoring Process
- 1. Ensure tests exist - Write tests if they don't
- Make small changes - One refactoring at a time
- Run tests after each change - Catch regressions immediately
- Commit frequently - Easy to revert if something breaks
- Review the diff - Make sure behavior hasn't changed
Refactoring Checklist
- - [ ] Tests pass before starting
- [ ] Each change is small and focused
- [ ] Tests pass after each change
- [ ] No behavior changes (only structure)
- [ ] Code is more readable than before
- [ ] Commit message explains the refactoring
代码重构
重构原则
何时重构
- - 在添加新功能之前(先让修改变得容易,再进行容易的修改)
- 在测试通过之后(红-绿-重构循环)
- 发现代码坏味道时
- 在代码审查反馈期间
何时不应重构
- - 没有测试覆盖的代码
- 在紧迫的截止日期且没有安全网的情况下
- 即将被替换的代码
- 当你不理解代码功能时
常见代码坏味道
长方法
typescript
// 重构前:方法承担过多职责
function processOrder(order: Order) {
// 100行验证、计算、通知、日志代码...
}
// 重构后:拆分为专注的方法
function processOrder(order: Order) {
validateOrder(order);
const total = calculateTotal(order);
saveOrder(order, total);
notifyCustomer(order);
}
深层嵌套条件
typescript
// 重构前:箭头形代码
function getDiscount(user: User, order: Order) {
if (user) {
if (user.isPremium) {
if (order.total > 100) {
if (order.items.length > 5) {
return 0.2;
}
}
}
}
return 0;
}
// 重构后:提前返回(卫语句)
function getDiscount(user: User, order: Order) {
if (!user) return 0;
if (!user.isPremium) return 0;
if (order.total <= 100) return 0;
if (order.items.length <= 5) return 0;
return 0.2;
}
基本类型偏执
typescript
// 重构前:到处使用基本类型
function createUser(name: string, email: string, phone: string) {
if (!email.includes(@)) throw new Error(无效邮箱);
// 更多验证...
}
// 重构后:值对象
class Email {
constructor(private value: string) {
if (!value.includes(@)) throw new Error(无效邮箱);
}
toString() { return this.value; }
}
function createUser(name: string, email: Email, phone: Phone) {
// Email已经过验证
}
依恋情结
typescript
// 重构前:方法大量使用另一个对象的数据
function calculateShipping(order: Order) {
const address = order.customer.address;
const weight = order.items.reduce((sum, i) => sum + i.weight, 0);
const distance = calculateDistance(address.zip);
return weight
distance 0.01;
}
// 重构后:将方法移至数据所在处
class Order {
calculateShipping() {
return this.totalWeight this.customer.shippingDistance 0.01;
}
}
重构技巧
提取方法
typescript
// 识别执行单一功能的代码块
// 将其移至具有描述性名称的新方法
// 用方法调用替换原始代码
function printReport(data: ReportData) {
// 提取此代码块...
const header = 报告:${data.title}\n日期:${data.date}\n${=.repeat(40)};
console.log(header);
// ...到方法中
printHeader(data);
}
用多态替换条件
typescript
// 重构前:根据类型进行switch
function getArea(shape: Shape) {
switch (shape.type) {
case circle: return Math.PI
shape.radius * 2;
case rectangle: return shape.width * shape.height;
case triangle: return shape.base * shape.height / 2;
}
}
// 重构后:多态类
interface Shape {
getArea(): number;
}
class Circle implements Shape {
constructor(private radius: number) {}
getArea() { return Math.PI this.radius * 2; }
}
class Rectangle implements Shape {
constructor(private width: number, private height: number) {}
getArea() { return this.width * this.height; }
}
引入参数对象
typescript
// 重构前:参数过多
function searchProducts(
query: string,
minPrice: number,
maxPrice: number,
category: string,
inStock: boolean,
sortBy: string,
sortOrder: string
) { ... }
// 重构后:参数对象
interface SearchParams {
query: string;
priceRange: { min: number; max: number };
category?: string;
inStock?: boolean;
sort?: { by: string; order: asc | desc };
}
function searchProducts(params: SearchParams) { ... }
用常量替换魔法数字
typescript
// 重构前
if (user.age >= 18 && order.total >= 50) {
applyDiscount(order, 0.1);
}
// 重构后
const MINIMUM_AGE = 18;
const DISCOUNT_THRESHOLD = 50;
const STANDARD_DISCOUNT = 0.1;
if (user.age >= MINIMUMAGE && order.total >= DISCOUNTTHRESHOLD) {
applyDiscount(order, STANDARD_DISCOUNT);
}
安全重构流程
- 1. 确保测试存在 - 如果没有则编写测试
- 进行小改动 - 每次只做一个重构
- 每次改动后运行测试 - 立即发现回归问题
- 频繁提交 - 出现问题易于回退
- 审查差异 - 确保行为未改变
重构检查清单
- - [ ] 开始前测试通过
- [ ] 每次改动小而专注
- [ ] 每次改动后测试通过
- [ ] 无行为变化(仅结构变化)
- [ ] 代码比以前更易读
- [ ] 提交信息解释重构内容