# 重构模式目录 (Refactoring Catalog)
> 常见重构模式的说明、适用场景与 before/after TypeScript 代码示例。
---
## Extract 模式(提取)
### Extract Function — 长函数拆分
**适用场景**:函数超过 30 行,包含多个逻辑段落。
```typescript
// ❌ Before: 验证 + 计算 + 支付全在一个函数
async function processOrder(order: Order) {
if (!order.items.length) throw new Error('空订单');
if (order.items.some(i => i.quantity <= 0)) throw new Error('数量无效');
let total = order.items.reduce((s, i) => s + i.unitPrice * i.quantity, 0);
total += total * 0.13;
const payment = await paymentService.create({ orderId: order.id, amount: total });
return { order, payment, total };
}
// ✅ After: 拆分为独立函数
function validateOrder(order: Order): void { /* 验证逻辑 */ }
function calculateTotal(items: OrderItem[]): number { /* 计算逻辑 */ }
async function processOrder(order: Order) {
validateOrder(order);
const total = calculateTotal(order.items);
const payment = await paymentService.create({ orderId: order.id, amount: total });
return { order, payment, total };
}
```
### Extract Component — React 大组件拆分
**适用场景**:组件 JSX 超过 150 行,包含多个独立 UI 区块。拆分为 `OrderFilterForm`、`OrderTable`、`Pagination` 等子组件,父组件仅负责组合与状态传递。
```typescript
// ✅ After: 父组件只做组合
function OrderPage() {
const [filter, setFilter] = useState('');
const { data: orders, total } = useOrders(filter);
return (
);
}
```
### Extract Hook — 自定义 Hook 提取
**适用场景**:多个组件共享相同的状态逻辑或副作用。
```typescript
// ✅ 通用异步数据 Hook(替代组件中重复的 data/loading/error + useEffect)
function useAsync(asyncFn: (signal: AbortSignal) => Promise, deps: unknown[]) {
const [state, setState] = useState<{ data: T | null; loading: boolean; error: Error | null }>(
{ data: null, loading: true, error: null }
);
useEffect(() => {
const ctrl = new AbortController();
asyncFn(ctrl.signal)
.then(data => setState({ data, loading: false, error: null }))
.catch(error => { if (!ctrl.signal.aborted) setState({ data: null, loading: false, error }); });
return () => ctrl.abort();
}, deps);
return state;
}
```
### Extract Service — API 调用逻辑抽取
**适用场景**:API 调用散布在组件中,缺少统一管理。
```typescript
// ✅ Service 层统一封装(替代组件中分散的 fetch 调用)
class OrderService {
create(data: CreateOrderDTO): Promise { return apiClient.post('/orders', data); }
getById(id: string): Promise { return apiClient.get(`/orders/${id}`); }
list(params: OrderQuery): Promise> {
return apiClient.get('/orders', { params });
}
}
export const orderService = new OrderService();
```
---
## Replace 模式(替换)
### Replace Conditional with Polymorphism — switch 转策略模式
**适用场景**:多处 switch/if-else 判断同一变量,分支可能增长。
```typescript
// ❌ Before: switch 散布在多个函数中
function calculateShipping(method: string, weight: number): number {
switch (method) {
case 'standard': return weight * 5;
case 'express': return weight * 10 + 15;
case 'overnight': return weight * 20 + 30;
}
}
// ✅ After: 策略模式,新增方式只需加一行
interface ShippingStrategy { cost(weight: number): number; days: number; }
const strategies: Record = {
standard: { cost: (w) => w * 5, days: 7 },
express: { cost: (w) => w * 10 + 15, days: 3 },
overnight: { cost: (w) => w * 20 + 30, days: 1 },
};
```
### Replace Temp with Query — 临时变量转计算属性
**适用场景**:临时变量缓存表达式结果,该表达式可封装为方法。
```typescript
// ❌ Before: 临时变量堆积
const basePrice = items.reduce((s, i) => s + i.price * i.qty, 0);
const discount = basePrice > 1000 ? 0.1 : 0;
const total = basePrice * (1 - discount) * 1.13;
// ✅ After: 计算属性类,逻辑可复用
class OrderCalculator {
constructor(private items: OrderItem[]) {}
get basePrice() { return this.items.reduce((s, i) => s + i.price * i.qty, 0); }
get discountRate() { return this.basePrice > 1000 ? 0.1 : 0; }
get total() { return this.basePrice * (1 - this.discountRate) * 1.13; }
}
```
### Replace Nested Conditionals with Guard Clauses — 卫语句简化
**适用场景**:多层嵌套 if-else 导致代码右漂(4 层 if 嵌套 → 卫语句提前返回)。
```typescript
// ✅ After: 卫语句提前返回,主逻辑清晰(替代 4 层嵌套 if-else)
function processPayment(order: Order | null, user: User) {
if (!order) return { success: false, reason: '订单不存在' };
if (order.status !== 'pending') return { success: false, reason: '状态不允许' };
if (!user.isActive) return { success: false, reason: '用户已停用' };
if (user.balance < order.total) return { success: false, reason: '余额不足' };
return { success: true };
}
```
---
## Introduce 模式(引入)
### Introduce Parameter Object — 多参数转配置对象
**适用场景**:函数参数超过 3-4 个。
```typescript
// ❌ Before: 8 个参数
function searchProducts(keyword: string, category: string, minPrice: number,
maxPrice: number, sortBy: string, sortOrder: 'asc'|'desc', page: number, size: number) {}
// ✅ After: 配置对象
interface SearchParams {
keyword: string;
category?: string;
priceRange?: { min: number; max: number };
sort?: { field: string; order: 'asc' | 'desc' };
pagination?: { page: number; size: number };
}
function searchProducts(params: SearchParams) { /* ... */ }
```
### Introduce Null Object — null 检查转空对象模式
**适用场景**:多处 `if (x != null)` 检查同一对象。
```typescript
// ❌ Before: 多处 null 检查
const name = user ? user.name : '游客';
const avatar = user ? user.avatarUrl : '/default.png';
// ✅ After: 预定义空对象
const GUEST: User = { id: '', name: '游客', avatarUrl: '/default.png', role: 'guest', permissions: [] };
function ensureUser(user: User | null): User { return user ?? GUEST; }
```
### Introduce Facade — 复杂接口转门面简化
**适用场景**:调用方需协调多个服务才能完成一个操作。将 inventory/order/payment/notification 多服务协调封装为 `CheckoutFacade.placeOrder()`,内部处理库存校验、支付扣款、补偿回滚和异步通知。
---
## 重构安全准则
1. **测试覆盖前置**:重构前确保目标代码有测试覆盖,没有则先补测试
2. **小步提交**:每次重构一个明确步骤,独立 commit,方便回滚
3. **单一职责每次**:一次只做一种重构操作,不混合多种手法
4. **重构与功能不混合**:重构 commit 与功能 commit 分离
### 重构信号速查表
| 信号 | 说明 | 对应模式 |
|------|------|----------|
| 函数超过 30 行 | 职责过多 | Extract Function |
| 组件超过 150 行 JSX | UI 逻辑混杂 | Extract Component |
| 相同逻辑出现 3+ 次 | 违反 DRY | Extract Function / Hook |
| switch/if-else 超过 5 分支 | 需要策略模式 | Replace Conditional |
| 函数参数超过 4 个 | 接口复杂 | Introduce Parameter Object |
| 嵌套超过 3 层 | 难以理解 | Guard Clauses |
| 多处 null 检查同一对象 | 空值逻辑散布 | Introduce Null Object |
| 调用方需协调 3+ 服务 | 耦合过高 | Introduce Facade |