dry-refactoring
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDRY 标准化流程:从识别到重构
DRY Standardized Process: From Identification to Refactoring
这个技能指导你系统性地应用 DRY (Don't Repeat Yourself) 原则,通过四步标准化流程消除代码重复,提升代码质量和可维护性。
This skill guides you to systematically apply the DRY (Don't Repeat Yourself) principle to eliminate code duplication and improve code quality and maintainability through a 4-step standardized process.
When to Use This Skill
When to Use This Skill
使用此技能当用户请求:
- 消除代码重复或冗余
- 重构有明显复制粘贴痕迹的代码
- 应用 DRY 原则优化代码库
- 识别并修复"代码坏味道"(如魔术数字、重复逻辑)
- 提取公共逻辑为可复用单元
- 改善代码的可维护性
关键触发词: DRY, 重复代码, 代码重复, 重构, 消除重复, 复制粘贴, 魔术数字, 代码坏味道, 抽象, 提取函数
Use this skill when users request to:
- Eliminate code duplication or redundancy
- Refactor code with obvious copy-paste traces
- Optimize codebase by applying the DRY principle
- Identify and fix "code smells" (such as magic numbers, repeated logic)
- Extract common logic into reusable units
- Improve code maintainability
Key Trigger Words: DRY, duplicate code, code repetition, refactoring, eliminate duplication, copy-paste, magic numbers, code smells, abstraction, extract function
核心思想
Core Idea
系统中的每一处知识都必须拥有一个单一、明确、权威的表示。
这意味着:
- 任何业务逻辑、算法或配置信息都应该只存在于代码库的一个地方
- 如果需要修改,你只需改这一个地方
- 修改会自动反映到所有使用该逻辑的地方
两次法则 (Rule of Two):
当你第二次写下几乎相同的代码块时,警钟就应该敲响。这是开始重构的信号。
Every piece of knowledge in a system must have a single, unambiguous, authoritative representation.
This means:
- Any business logic, algorithm, or configuration information should exist in only one place in the codebase
- When modifications are needed, you only need to change this one place
- The changes will be automatically reflected in all places using this logic
Rule of Two:
When you write almost the same code block for the second time, an alarm should go off. This is a signal to start refactoring.
四步标准化流程
4-Step Standardized Process
这是一个可在编码任何阶段应用的微循环。严格按照步骤执行,确保重构的安全性和有效性。
This is a micro-cycle that can be applied at any stage of coding. Follow the steps strictly to ensure the safety and effectiveness of refactoring.
第一步:识别重复 (Identify Repetition)
Step 1: Identify Repetition
目标: 像侦探一样,对代码中的"坏味道"保持警惕,找出所有重复。
Goal: Act like a detective, stay alert to "code smells" in the code, and find all repetitions.
1.1 明显的重复
1.1 Obvious Repetition
直接复制粘贴:
- 两块或多块代码长得几乎一模一样
- 只有变量名或少数值不同
- 这是最明显、最需要被消除的重复
示例:
javascript
// 重复 1
function calculateOrderDiscount(orderTotal) {
if (orderTotal > 100) {
return orderTotal * 0.1;
}
return 0;
}
// 重复 2
function calculateCouponDiscount(couponTotal) {
if (couponTotal > 100) {
return couponTotal * 0.1;
}
return 0;
}"魔术数字"或字符串:
- 同一个配置值或字符串在多处以字面量形式出现
- 例如:、
0.08、"http://api.example.com"100
示例:
python
undefinedDirect Copy-Paste:
- Two or more code blocks are almost identical
- Only variable names or a few values are different
- This is the most obvious type of repetition that needs to be eliminated
Example:
javascript
// 重复 1
function calculateOrderDiscount(orderTotal) {
if (orderTotal > 100) {
return orderTotal * 0.1;
}
return 0;
}
// 重复 2
function calculateCouponDiscount(couponTotal) {
if (couponTotal > 100) {
return couponTotal * 0.1;
}
return 0;
}"Magic Numbers" or Strings:
- The same configuration value or string appears in multiple places as literals
- For example: ,
0.08,"http://api.example.com"100
Example:
python
undefined魔术数字重复
魔术数字重复
def calculate_tax_1(amount):
return amount * 0.08 # ❌ 魔术数字
def calculate_tax_2(amount):
return amount * 0.08 # ❌ 再次出现
def calculate_total(amount):
tax = amount * 0.08 # ❌ 第三次
return amount + tax
undefineddef calculate_tax_1(amount):
return amount * 0.08 # ❌ 魔术数字
def calculate_tax_2(amount):
return amount * 0.08 # ❌ 再次出现
def calculate_total(amount):
tax = amount * 0.08 # ❌ 第三次
return amount + tax
undefined1.2 语义上的重复
1.2 Semantic Repetition
结构性重复:
- 代码结构相似,但具体变量名或值不同
- 多个 if-else 结构都在做类似的条件判断和赋值
示例:
typescript
// 结构性重复
function processUserData(user: User) {
if (user.age >= 18) {
user.status = 'adult';
} else {
user.status = 'minor';
}
}
function processProductData(product: Product) {
if (product.price >= 100) {
product.category = 'premium';
} else {
product.category = 'standard';
}
}逻辑重复:
- 两个不同的函数,代码看起来不一样
- 但它们在业务逻辑层面实现的是同一个目标
示例:
javascript
// 逻辑重复:都在计算折扣,只是来源不同
function applyMembershipDiscount(price, memberLevel) {
const discountRates = { gold: 0.2, silver: 0.1, bronze: 0.05 };
return price * (1 - (discountRates[memberLevel] || 0));
}
function applySeasonalDiscount(price, season) {
const discountRates = { winter: 0.2, spring: 0.1, summer: 0.05 };
return price * (1 - (discountRates[season] || 0));
}Structural Repetition:
- Code structures are similar, but specific variable names or values are different
- Multiple if-else structures perform similar conditional judgments and assignments
Example:
typescript
// 结构性重复
function processUserData(user: User) {
if (user.age >= 18) {
user.status = 'adult';
} else {
user.status = 'minor';
}
}
function processProductData(product: Product) {
if (product.price >= 100) {
product.category = 'premium';
} else {
product.category = 'standard';
}
}Logical Repetition:
- Two different functions look different in code
- But they achieve the same goal at the business logic level
Example:
javascript
// 逻辑重复:都在计算折扣,只是来源不同
function applyMembershipDiscount(price, memberLevel) {
const discountRates = { gold: 0.2, silver: 0.1, bronze: 0.05 };
return price * (1 - (discountRates[memberLevel] || 0));
}
function applySeasonalDiscount(price, season) {
const discountRates = { winter: 0.2, spring: 0.1, summer: 0.05 };
return price * (1 - (discountRates[season] || 0));
}识别清单
Identification Checklist
当你审查代码时,检查以下信号:
- 复制粘贴的代码块(完全相同或高度相似)
- 相同的数字、字符串在多处出现
- 相似的 if-else 或 switch-case 结构
- 功能相似但命名不同的函数
- 相同的算法在不同地方重新实现
- 相同的验证逻辑分散在多个地方
💡 Action: 使用搜索功能查找重复的字面量、相似的函数名模式。记录所有重复出现的位置。
When reviewing code, check for the following signals:
- Copy-pasted code blocks (identical or highly similar)
- Same numbers or strings appearing in multiple places
- Similar if-else or switch-case structures
- Functions with similar functions but different names
- Same algorithm reimplemented in different places
- Same validation logic scattered in multiple places
💡 Action: Use search functions to find repeated literals and similar function name patterns. Record all locations where repetitions occur.
第二步:抽象逻辑 (Abstract the Logic)
Step 2: Abstract the Logic
目标: 将重复的逻辑提取出来,封装到一个独立、可复用的单元中。
Goal: Extract repeated logic and encapsulate it into an independent, reusable unit.
2.1 识别可变与不变部分
2.1 Identify Variable and Invariant Parts
不变部分:
- 这是重复的核心逻辑
- 每次重复时都保持不变的代码
- 这将成为你的抽象主体
可变部分:
- 每次重复时发生变化的东西
- 不同的值、变量名、配置
- 这些将成为函数或类的参数
分析示例:
javascript
// 原始重复代码
const userEmail = validateEmail(user.email);
const adminEmail = validateEmail(admin.email);
const supportEmail = validateEmail(support.email);
// 分析:
// 不变部分:validateEmail() 调用
// 可变部分:不同的 email 值Invariant Parts:
- This is the core repeated logic
- Code that remains unchanged during each repetition
- This will become the main body of your abstraction
Variable Parts:
- Things that change during each repetition
- Different values, variable names, configurations
- These will become the parameters of functions or classes
Analysis Example:
javascript
// 原始重复代码
const userEmail = validateEmail(user.email);
const adminEmail = validateEmail(admin.email);
const supportEmail = validateEmail(support.email);
// 分析:
// 不变部分:validateEmail() 调用
// 可变部分:不同的 email 值2.2 选择合适的抽象形式
2.2 Choose the Right Abstraction Form
根据重复的特点,选择最合适的抽象方式:
| 抽象形式 | 适用场景 | 示例 |
|---|---|---|
| 函数 (Function) | 封装一段算法或行为 | 计算折扣、验证输入、格式化数据 |
| 类 (Class) | 封装行为 + 关联状态 | 用户管理器、数据处理器、配置管理器 |
| 模块/组件 | 一组相关的函数、类和配置 | 认证模块、日志模块、API 客户端 |
| 配置文件/常量 | 重复的魔术数字或字符串 | API 端点、税率、阈值 |
| 高阶函数 | 重复的控制流程或模式 | 重试逻辑、缓存包装、错误处理 |
Choose the most appropriate abstraction method based on the characteristics of the repetition:
| Abstraction Form | Applicable Scenarios | Example |
|---|---|---|
| Function | Encapsulate an algorithm or behavior | Calculate discounts, validate inputs, format data |
| Class | Encapsulate behavior + associated state | User manager, data processor, configuration manager |
| Module/Component | A set of related functions, classes, and configurations | Authentication module, logging module, API client |
| Configuration File/Constant | Repeated magic numbers or strings | API endpoints, tax rates, thresholds |
| Higher-Order Function | Repeated control flows or patterns | Retry logic, caching wrapper, error handling |
2.3 设计抽象接口
2.3 Design the Abstraction Interface
函数抽象示例:
python
undefinedFunction Abstraction Example:
python
undefined❌ 重复代码
❌ 重复代码
def process_user_order(user_id, order_data):
user = db.query(User).filter_by(id=user_id).first()
if not user:
raise ValueError("User not found")
# 处理订单...
def process_user_payment(user_id, payment_data):
user = db.query(User).filter_by(id=user_id).first()
if not user:
raise ValueError("User not found")
# 处理支付...
def process_user_order(user_id, order_data):
user = db.query(User).filter_by(id=user_id).first()
if not user:
raise ValueError("User not found")
# 处理订单...
def process_user_payment(user_id, payment_data):
user = db.query(User).filter_by(id=user_id).first()
if not user:
raise ValueError("User not found")
# 处理支付...
✅ 抽象后
✅ 抽象后
def get_user_or_error(user_id):
"""不变部分:获取用户并验证"""
user = db.query(User).filter_by(id=user_id).first()
if not user:
raise ValueError("User not found")
return user
def process_user_order(user_id, order_data):
user = get_user_or_error(user_id) # 可变部分:user_id
# 处理订单...
def process_user_payment(user_id, payment_data):
user = get_user_or_error(user_id) # 可变部分:user_id
# 处理支付...
**常量抽象示例**:
```javascript
// ❌ 魔术数字
function calculateTax(amount) {
return amount * 0.08;
}
function displayTaxInfo(amount) {
console.log(`Tax (8%): $${amount * 0.08}`);
}
// ✅ 抽象为常量
const TAX_RATE = 0.08;
function calculateTax(amount) {
return amount * TAX_RATE;
}
function displayTaxInfo(amount) {
console.log(`Tax (${TAX_RATE * 100}%): $${amount * TAX_RATE}`);
}类抽象示例:
typescript
// ❌ 重复的状态和行为
const userCache = new Map();
function getUserFromCache(id: string) { /*...*/ }
function setUserInCache(id: string, user: User) { /*...*/ }
const productCache = new Map();
function getProductFromCache(id: string) { /*...*/ }
function setProductInCache(id: string, product: Product) { /*...*/ }
// ✅ 抽象为类
class Cache<T> {
private store = new Map<string, T>();
get(id: string): T | undefined {
return this.store.get(id);
}
set(id: string, value: T): void {
this.store.set(id, value);
}
}
const userCache = new Cache<User>();
const productCache = new Cache<Product>();def get_user_or_error(user_id):
"""不变部分:获取用户并验证"""
user = db.query(User).filter_by(id=user_id).first()
if not user:
raise ValueError("User not found")
return user
def process_user_order(user_id, order_data):
user = get_user_or_error(user_id) # 可变部分:user_id
# 处理订单...
def process_user_payment(user_id, payment_data):
user = get_user_or_error(user_id) # 可变部分:user_id
# 处理支付...
**Constant Abstraction Example**:
```javascript
// ❌ 魔术数字
function calculateTax(amount) {
return amount * 0.08;
}
function displayTaxInfo(amount) {
console.log(`Tax (8%): $${amount * 0.08}`);
}
// ✅ 抽象为常量
const TAX_RATE = 0.08;
function calculateTax(amount) {
return amount * TAX_RATE;
}
function displayTaxInfo(amount) {
console.log(`Tax (${TAX_RATE * 100}%): $${amount * TAX_RATE}`);
}Class Abstraction Example:
typescript
// ❌ 重复的状态和行为
const userCache = new Map();
function getUserFromCache(id: string) { /*...*/ }
function setUserInCache(id: string, user: User) { /*...*/ }
const productCache = new Map();
function getProductFromCache(id: string) { /*...*/ }
function setProductInCache(id: string, product: Product) { /*...*/ }
// ✅ 抽象为类
class Cache<T> {
private store = new Map<string, T>();
get(id: string): T | undefined {
return this.store.get(id);
}
set(id: string, value: T): void {
this.store.set(id, value);
}
}
const userCache = new Cache<User>();
const productCache = new Cache<Product>();抽象设计原则
Abstraction Design Principles
Do:
- ✅ 参数化可变部分(值、配置、行为)
- ✅ 保持接口简单(参数数量 ≤ 4 个)
- ✅ 使用描述性命名(说明"做什么"而非"怎么做")
- ✅ 考虑未来的扩展性(但不要过度设计)
Don't:
- ❌ 创建过于通用的抽象("万能函数")
- ❌ 过早抽象(只有一次使用时不要抽象)
- ❌ 忽略性能影响(例如不必要的函数调用开销)
- ❌ 使用难以理解的抽象(增加认知负担)
💡 Action: 创建一个新的函数、类或配置文件,将"不变部分"放进去,将"可变部分"定义为参数。
Do:
- ✅ Parameterize variable parts (values, configurations, behaviors)
- ✅ Keep interfaces simple (number of parameters ≤ 4)
- ✅ Use descriptive names (explain "what it does" instead of "how it does it")
- ✅ Consider future extensibility (but don't over-design)
Don't:
- ❌ Create overly general abstractions ("one-size-fits-all functions")
- ❌ Abstract prematurely (don't abstract if it's only used once)
- ❌ Ignore performance impacts (e.g., unnecessary function call overhead)
- ❌ Use hard-to-understand abstractions (increase cognitive load)
💡 Action: Create a new function, class, or configuration file, put the "invariant parts" into it, and define the "variable parts" as parameters.
第三步:替换实现 (Replace the Implementation)
Step 3: Replace the Implementation
目标: 用新的抽象单元替换所有旧的重复代码。
Goal: Replace all old repetitive code with the new abstraction unit.
3.1 系统性替换
3.1 Systematic Replacement
步骤:
- 定位所有重复点:回到第一步记录的所有位置
- 逐一替换:删除旧代码,调用新抽象
- 传入正确参数:确保参数对应正确
- 保持行为一致:确保替换前后功能完全相同
替换示例:
Before (重复代码):
python
undefinedSteps:
- Locate all repetition points: Go back to all locations recorded in Step 1
- Replace one by one: Delete old code and call the new abstraction
- Pass correct parameters: Ensure parameters are correctly mapped
- Maintain consistent behavior: Ensure functions are exactly the same before and after replacement
Replacement Example:
Before (Duplicate Code):
python
undefined位置 1: user_service.py
位置 1: user_service.py
def create_user(data):
if not data.get('email'):
return {'error': 'Email is required'}, 400
if not data.get('password'):
return {'error': 'Password is required'}, 400
# 创建用户...
def create_user(data):
if not data.get('email'):
return {'error': 'Email is required'}, 400
if not data.get('password'):
return {'error': 'Password is required'}, 400
# 创建用户...
位置 2: product_service.py
位置 2: product_service.py
def create_product(data):
if not data.get('name'):
return {'error': 'Name is required'}, 400
if not data.get('price'):
return {'error': 'Price is required'}, 400
# 创建产品...
**After (使用抽象)**:
```pythondef create_product(data):
if not data.get('name'):
return {'error': 'Name is required'}, 400
if not data.get('price'):
return {'error': 'Price is required'}, 400
# 创建产品...
**After (Using Abstraction)**:
```python新抽象: validation_utils.py
新抽象: validation_utils.py
def validate_required_fields(data, required_fields):
"""验证必填字段"""
for field in required_fields:
if not data.get(field):
return {'error': f'{field.capitalize()} is required'}, 400
return None
def validate_required_fields(data, required_fields):
"""验证必填字段"""
for field in required_fields:
if not data.get(field):
return {'error': f'{field.capitalize()} is required'}, 400
return None
位置 1: user_service.py (已替换)
位置 1: user_service.py (已替换)
def create_user(data):
error = validate_required_fields(data, ['email', 'password'])
if error:
return error
# 创建用户...
def create_user(data):
error = validate_required_fields(data, ['email', 'password'])
if error:
return error
# 创建用户...
位置 2: product_service.py (已替换)
位置 2: product_service.py (已替换)
def create_product(data):
error = validate_required_fields(data, ['name', 'price'])
if error:
return error
# 创建产品...
undefineddef create_product(data):
error = validate_required_fields(data, ['name', 'price'])
if error:
return error
# 创建产品...
undefined3.2 处理边缘情况
3.2 Handle Edge Cases
有时候重复代码之间存在细微差异,需要特殊处理:
策略 1:添加可选参数
javascript
// 大部分重复,但有一个地方需要额外日志
function processData(data, options = {}) {
// 通用处理...
if (options.enableLogging) {
console.log('Processing:', data);
}
return result;
}
// 使用
processData(data1); // 无日志
processData(data2, { enableLogging: true }); // 有日志策略 2:回调函数
typescript
// 核心流程相同,但中间步骤不同
function processWithCustomStep<T>(
data: T,
customStep: (item: T) => T
): T {
// 前置处理
const prepared = prepare(data);
// 可变的自定义步骤
const processed = customStep(prepared);
// 后置处理
return finalize(processed);
}
// 使用
processWithCustomStep(userData, (user) => validateUser(user));
processWithCustomStep(productData, (product) => enrichProduct(product));策略 3:保留特殊情况
python
undefinedSometimes there are subtle differences between duplicate code that require special handling:
Strategy 1: Add Optional Parameters
javascript
// 大部分重复,但有一个地方需要额外日志
function processData(data, options = {}) {
// 通用处理...
if (options.enableLogging) {
console.log('Processing:', data);
}
return result;
}
// 使用
processData(data1); // 无日志
processData(data2, { enableLogging: true }); // 有日志Strategy 2: Callback Functions
typescript
// 核心流程相同,但中间步骤不同
function processWithCustomStep<T>(
data: T,
customStep: (item: T) => T
): T {
// 前置处理
const prepared = prepare(data);
// 可变的自定义步骤
const processed = customStep(prepared);
// 后置处理
return finalize(processed);
}
// 使用
processWithCustomStep(userData, (user) => validateUser(user));
processWithCustomStep(productData, (product) => enrichProduct(product));Strategy 3: Keep Special Cases
python
undefined如果某个重复有本质上的不同,考虑保留它
如果某个重复有本质上的不同,考虑保留它
def process_standard_order(order):
return apply_dry_abstraction(order, 'standard')
def process_vip_order(order):
# VIP 订单有完全不同的业务逻辑,不强行抽象
# 保留独立实现
pass
undefineddef process_standard_order(order):
return apply_dry_abstraction(order, 'standard')
def process_vip_order(order):
# VIP 订单有完全不同的业务逻辑,不强行抽象
# 保留独立实现
pass
undefined替换清单
Replacement Checklist
- 确认所有重复点都已替换(不要遗漏)
- 删除旧的重复代码(避免混用新旧方式)
- 检查导入语句和依赖关系
- 确保参数顺序和类型正确
- 处理了所有边缘情况
⚠️ 警告: 如果只替换了一部分,你就创造了另一种不一致,情况可能更糟。确保全部替换或全部不替换。
💡 Action: 使用 IDE 的"查找所有引用"功能,确保没有遗漏任何重复点。
- Confirm all repetition points have been replaced (don't miss any)
- Delete old duplicate code (avoid mixing old and new methods)
- Check import statements and dependencies
- Ensure parameter order and types are correct
- Handled all edge cases
⚠️ Warning: If you only replace part of the code, you create another type of inconsistency, which may make the situation worse. Ensure all are replaced or none are replaced.
💡 Action: Use the IDE's "Find All References" feature to ensure no repetition points are missed.
第四步:验证与测试 (Verify and Test)
Step 4: Verify and Test
目标: 确保重构没有破坏任何功能,程序行为在重构前后完全一致。
Goal: Ensure refactoring does not break any functionality, and the program behavior is exactly the same before and after refactoring.
4.1 单元测试
4.1 Unit Testing
为你新创建的抽象编写独立的单元测试:
测试覆盖要点:
- ✅ 正常输入的正确输出
- ✅ 边界值测试(空值、最大值、最小值)
- ✅ 异常输入的错误处理
- ✅ 不同参数组合的行为
示例:
python
undefinedWrite independent unit tests for your newly created abstraction:
Test Coverage Points:
- ✅ Correct output for normal inputs
- ✅ Boundary value tests (empty values, maximum values, minimum values)
- ✅ Error handling for abnormal inputs
- ✅ Behavior of different parameter combinations
Example:
python
undefined抽象函数
抽象函数
def calculate_discount(price, discount_rate):
"""计算折扣后价格"""
if not 0 <= discount_rate <= 1:
raise ValueError("Discount rate must be between 0 and 1")
return price * (1 - discount_rate)
def calculate_discount(price, discount_rate):
"""计算折扣后价格"""
if not 0 <= discount_rate <= 1:
raise ValueError("Discount rate must be between 0 and 1")
return price * (1 - discount_rate)
单元测试
单元测试
def test_calculate_discount():
# 正常情况
assert calculate_discount(100, 0.1) == 90
assert calculate_discount(100, 0) == 100
# 边界情况
assert calculate_discount(0, 0.5) == 0
assert calculate_discount(100, 1) == 0
# 异常情况
with pytest.raises(ValueError):
calculate_discount(100, 1.5)
with pytest.raises(ValueError):
calculate_discount(100, -0.1)undefineddef test_calculate_discount():
# 正常情况
assert calculate_discount(100, 0.1) == 90
assert calculate_discount(100, 0) == 100
# 边界情况
assert calculate_discount(0, 0.5) == 0
assert calculate_discount(100, 1) == 0
# 异常情况
with pytest.raises(ValueError):
calculate_discount(100, 1.5)
with pytest.raises(ValueError):
calculate_discount(100, -0.1)undefined4.2 集成测试
4.2 Integration Testing
运行那些覆盖了被修改代码区域的集成测试:
bash
undefinedRun integration tests that cover the modified code areas:
bash
undefined运行特定模块的测试
运行特定模块的测试
pytest tests/test_user_service.py
pytest tests/test_product_service.py
pytest tests/test_user_service.py
pytest tests/test_product_service.py
或运行整个测试套件
或运行整个测试套件
npm test
pytest
**检查要点**:
- [ ] 所有测试都通过
- [ ] 没有新的失败或错误
- [ ] 性能没有显著下降
- [ ] 覆盖率没有降低npm test
pytest
**Checklist**:
- [ ] All tests pass
- [ ] No new failures or errors
- [ ] Performance has not significantly degraded
- [ ] Coverage has not decreased4.3 手动验证
4.3 Manual Verification
如果没有自动化测试(或测试覆盖不足),进行手动验证:
验证清单:
- 启动应用程序,检查是否正常运行
- 测试被修改的功能(通过 UI 或 API)
- 检查日志输出是否正常
- 测试错误场景(无效输入、边界条件)
- 在不同环境中测试(开发、测试、预发布)
If there are no automated tests (or test coverage is insufficient), perform manual verification:
Verification Checklist:
- Start the application and check if it runs normally
- Test the modified functionality (via UI or API)
- Check if log output is normal
- Test error scenarios (invalid inputs, boundary conditions)
- Test in different environments (development, testing, pre-release)
4.4 性能验证
4.4 Performance Verification
确保抽象没有引入性能问题:
python
import timeEnsure abstraction does not introduce performance issues:
python
import time性能测试
性能测试
def benchmark_function(func, *args, iterations=1000):
start = time.time()
for _ in range(iterations):
func(*args)
end = time.time()
return (end - start) / iterations
def benchmark_function(func, *args, iterations=1000):
start = time.time()
for _ in range(iterations):
func(*args)
end = time.time()
return (end - start) / iterations
对比重构前后
对比重构前后
old_time = benchmark_function(old_implementation, test_data)
new_time = benchmark_function(new_implementation, test_data)
print(f"Old: {old_time:.6f}s, New: {new_time:.6f}s")
print(f"Difference: {((new_time - old_time) / old_time * 100):.2f}%")
undefinedold_time = benchmark_function(old_implementation, test_data)
new_time = benchmark_function(new_implementation, test_data)
print(f"Old: {old_time:.6f}s, New: {new_time:.6f}s")
print(f"Difference: {((new_time - old_time) / old_time * 100):.2f}%")
undefined4.5 代码审查
4.5 Code Review
如果在团队中工作,进行代码审查:
审查要点:
- 抽象是否合理且易于理解?
- 命名是否清晰且符合约定?
- 是否有遗漏的重复点?
- 是否过度抽象或设计复杂?
- 文档和注释是否充分?
💡 Action: 运行所有相关测试,确保程序的外部行为在重构前后完全一致。没有测试?现在是编写测试的最佳时机。
If working in a team, conduct code review:
Review Points:
- Is the abstraction reasonable and easy to understand?
- Are names clear and consistent with conventions?
- Are there any missed repetition points?
- Is it over-abstracted or overly complex?
- Are documentation and comments sufficient?
💡 Action: Run all relevant tests to ensure the external behavior of the program is exactly the same before and after refactoring. No tests? Now is the best time to write them.
完整示例:从头到尾
Complete Example: From Start to Finish
场景:电商系统的折扣计算
Scenario: Discount Calculation in E-Commerce System
原始代码(存在重复)
Original Code (With Duplication)
javascript
// order_service.js
function calculateOrderTotal(order) {
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
// 会员折扣
if (order.memberLevel === 'gold') {
total = total * 0.8; // ❌ 魔术数字
} else if (order.memberLevel === 'silver') {
total = total * 0.9; // ❌ 魔术数字
}
return total;
}
// cart_service.js
function calculateCartTotal(cart) {
let total = 0;
for (const item of cart.items) {
total += item.price * item.quantity; // ❌ 重复计算逻辑
}
// 优惠券折扣
if (cart.couponType === 'premium') {
total = total * 0.8; // ❌ 重复的折扣计算
} else if (cart.couponType === 'standard') {
total = total * 0.9; // ❌ 重复的折扣计算
}
return total;
}javascript
// order_service.js
function calculateOrderTotal(order) {
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
// 会员折扣
if (order.memberLevel === 'gold') {
total = total * 0.8; // ❌ 魔术数字
} else if (order.memberLevel === 'silver') {
total = total * 0.9; // ❌ 魔术数字
}
return total;
}
// cart_service.js
function calculateCartTotal(cart) {
let total = 0;
for (const item of cart.items) {
total += item.price * item.quantity; // ❌ 重复计算逻辑
}
// 优惠券折扣
if (cart.couponType === 'premium') {
total = total * 0.8; // ❌ 重复的折扣计算
} else if (cart.couponType === 'standard') {
total = total * 0.9; // ❌ 重复的折扣计算
}
return total;
}步骤 1:识别重复
Step 1: Identify Repetition
发现的重复:
- 计算商品总价的循环逻辑(结构重复)
- 折扣计算逻辑(逻辑重复)
- 魔术数字 和
0.8(明显重复)0.9
Identified Repetitions:
- Loop logic for calculating total item price (structural repetition)
- Discount calculation logic (logical repetition)
- Magic numbers and
0.8(obvious repetition)0.9
步骤 2:抽象逻辑
Step 2: Abstract the Logic
javascript
// pricing_utils.js (新建)
// 抽象 1:商品总价计算
function calculateItemsTotal(items) {
return items.reduce((total, item) => {
return total + (item.price * item.quantity);
}, 0);
}
// 抽象 2:折扣配置(消除魔术数字)
const DISCOUNT_RATES = {
membership: {
gold: 0.2, // 20% off
silver: 0.1, // 10% off
bronze: 0.05 // 5% off
},
coupon: {
premium: 0.2, // 20% off
standard: 0.1, // 10% off
basic: 0.05 // 5% off
}
};
// 抽象 3:应用折扣
function applyDiscount(amount, discountRate) {
if (discountRate < 0 || discountRate > 1) {
throw new Error('Invalid discount rate');
}
return amount * (1 - discountRate);
}
// 抽象 4:获取折扣率
function getDiscountRate(category, level) {
return DISCOUNT_RATES[category]?.[level] || 0;
}
export { calculateItemsTotal, applyDiscount, getDiscountRate };javascript
// pricing_utils.js (新建)
// 抽象 1:商品总价计算
function calculateItemsTotal(items) {
return items.reduce((total, item) => {
return total + (item.price * item.quantity);
}, 0);
}
// 抽象 2:折扣配置(消除魔术数字)
const DISCOUNT_RATES = {
membership: {
gold: 0.2, // 20% off
silver: 0.1, // 10% off
bronze: 0.05 // 5% off
},
coupon: {
premium: 0.2, // 20% off
standard: 0.1, // 10% off
basic: 0.05 // 5% off
}
};
// 抽象 3:应用折扣
function applyDiscount(amount, discountRate) {
if (discountRate < 0 || discountRate > 1) {
throw new Error('Invalid discount rate');
}
return amount * (1 - discountRate);
}
// 抽象 4:获取折扣率
function getDiscountRate(category, level) {
return DISCOUNT_RATES[category]?.[level] || 0;
}
export { calculateItemsTotal, applyDiscount, getDiscountRate };步骤 3:替换实现
Step 3: Replace the Implementation
javascript
// order_service.js (重构后)
import { calculateItemsTotal, applyDiscount, getDiscountRate } from './pricing_utils.js';
function calculateOrderTotal(order) {
const subtotal = calculateItemsTotal(order.items);
const discountRate = getDiscountRate('membership', order.memberLevel);
return applyDiscount(subtotal, discountRate);
}
// cart_service.js (重构后)
import { calculateItemsTotal, applyDiscount, getDiscountRate } from './pricing_utils.js';
function calculateCartTotal(cart) {
const subtotal = calculateItemsTotal(cart.items);
const discountRate = getDiscountRate('coupon', cart.couponType);
return applyDiscount(subtotal, discountRate);
}javascript
// order_service.js (重构后)
import { calculateItemsTotal, applyDiscount, getDiscountRate } from './pricing_utils.js';
function calculateOrderTotal(order) {
const subtotal = calculateItemsTotal(order.items);
const discountRate = getDiscountRate('membership', order.memberLevel);
return applyDiscount(subtotal, discountRate);
}
// cart_service.js (重构后)
import { calculateItemsTotal, applyDiscount, getDiscountRate } from './pricing_utils.js';
function calculateCartTotal(cart) {
const subtotal = calculateItemsTotal(cart.items);
const discountRate = getDiscountRate('coupon', cart.couponType);
return applyDiscount(subtotal, discountRate);
}步骤 4:验证与测试
Step 4: Verify and Test
javascript
// pricing_utils.test.js
import { calculateItemsTotal, applyDiscount, getDiscountRate } from './pricing_utils.js';
describe('Pricing Utils', () => {
describe('calculateItemsTotal', () => {
it('should calculate total for multiple items', () => {
const items = [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 3 }
];
expect(calculateItemsTotal(items)).toBe(35);
});
it('should return 0 for empty items', () => {
expect(calculateItemsTotal([])).toBe(0);
});
});
describe('applyDiscount', () => {
it('should apply 20% discount correctly', () => {
expect(applyDiscount(100, 0.2)).toBe(80);
});
it('should throw error for invalid discount rate', () => {
expect(() => applyDiscount(100, 1.5)).toThrow('Invalid discount rate');
});
});
describe('getDiscountRate', () => {
it('should return correct membership discount', () => {
expect(getDiscountRate('membership', 'gold')).toBe(0.2);
});
it('should return 0 for unknown level', () => {
expect(getDiscountRate('membership', 'unknown')).toBe(0);
});
});
});
// 运行测试
// npm test pricing_utils.test.js重构成果:
- ✅ 消除了所有重复代码
- ✅ 魔术数字集中管理
- ✅ 每个函数职责单一
- ✅ 易于测试和维护
- ✅ 如果需要添加新的会员等级或折扣类型,只需修改
DISCOUNT_RATES
javascript
// pricing_utils.test.js
import { calculateItemsTotal, applyDiscount, getDiscountRate } from './pricing_utils.js';
describe('Pricing Utils', () => {
describe('calculateItemsTotal', () => {
it('should calculate total for multiple items', () => {
const items = [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 3 }
];
expect(calculateItemsTotal(items)).toBe(35);
});
it('should return 0 for empty items', () => {
expect(calculateItemsTotal([])).toBe(0);
});
});
describe('applyDiscount', () => {
it('should apply 20% discount correctly', () => {
expect(applyDiscount(100, 0.2)).toBe(80);
});
it('should throw error for invalid discount rate', () => {
expect(() => applyDiscount(100, 1.5)).toThrow('Invalid discount rate');
});
});
describe('getDiscountRate', () => {
it('should return correct membership discount', () => {
expect(getDiscountRate('membership', 'gold')).toBe(0.2);
});
it('should return 0 for unknown level', () => {
expect(getDiscountRate('membership', 'unknown')).toBe(0);
});
});
});
// 运行测试
// npm test pricing_utils.test.jsRefactoring Results:
- ✅ Eliminated all duplicate code
- ✅ Magic numbers are centrally managed
- ✅ Each function has a single responsibility
- ✅ Easy to test and maintain
- ✅ If new membership levels or discount types need to be added, only modify
DISCOUNT_RATES
常见陷阱与解决方案
Common Pitfalls and Solutions
陷阱 1:过度抽象 (Over-Abstraction)
Pitfall 1: Over-Abstraction
症状:创建了过于通用、难以理解的抽象。
示例:
javascript
// ❌ 过度抽象
function universalProcessor(data, options, callbacks, config, meta) {
// 100 行通用处理逻辑...
}
// ✅ 合理抽象
function processUserData(user) {
return validate(user) && transform(user);
}解决方案:
- 只在有明确重复时才抽象
- 保持抽象简单明了
- 如果参数超过 4 个,考虑重新设计
Symptoms: Created overly general, hard-to-understand abstractions.
Example:
javascript
// ❌ 过度抽象
function universalProcessor(data, options, callbacks, config, meta) {
// 100 行通用处理逻辑...
}
// ✅ 合理抽象
function processUserData(user) {
return validate(user) && transform(user);
}Solutions:
- Only abstract when there is clear repetition
- Keep abstractions simple and clear
- If there are more than 4 parameters, consider redesigning
陷阱 2:不完全替换
Pitfall 2: Incomplete Replacement
症状:只替换了部分重复点,留下了一些旧代码。
后果:
- 代码库中存在新旧两种实现
- 未来修改时容易遗漏
- 造成新的不一致
解决方案:
- 使用全局搜索确保找到所有重复
- 一次性完成所有替换
- 使用 linter 或静态分析工具检测未使用的代码
Symptoms: Only replaced part of the repetition points, leaving some old code.
Consequences:
- Both old and new implementations exist in the codebase
- Easy to miss during future modifications
- Causes new inconsistencies
Solutions:
- Use global search to ensure all repetitions are found
- Complete all replacements at once
- Use linters or static analysis tools to detect unused code
陷阱 3:忽略性能影响
Pitfall 3: Ignoring Performance Impacts
症状:抽象引入了不必要的性能开销。
示例:
python
undefinedSymptoms: Abstraction introduces unnecessary performance overhead.
Example:
python
undefined❌ 每次调用都重新编译正则表达式
❌ 每次调用都重新编译正则表达式
def validate_email(email):
return re.match(r'^[\w.-]+@[\w.-]+.\w+$', email)
def validate_email(email):
return re.match(r'^[\w.-]+@[\w.-]+.\w+$', email)
✅ 复用编译后的正则表达式
✅ 复用编译后的正则表达式
EMAIL_PATTERN = re.compile(r'^[\w.-]+@[\w.-]+.\w+$')
def validate_email(email):
return EMAIL_PATTERN.match(email)
**解决方案**:
- 对性能敏感的代码进行基准测试
- 使用缓存、记忆化等优化技术
- 必要时使用性能分析工具EMAIL_PATTERN = re.compile(r'^[\w.-]+@[\w.-]+.\w+$')
def validate_email(email):
return EMAIL_PATTERN.match(email)
**Solutions**:
- Benchmark performance-sensitive code
- Use optimization techniques like caching and memoization
- Use performance analysis tools when necessary陷阱 4:破坏封装性
Pitfall 4: Breaking Encapsulation
症状:抽象暴露了过多内部实现细节。
示例:
typescript
// ❌ 暴露内部状态
class UserManager {
public users: Map<string, User>; // 直接暴露内部数据结构
getUser(id: string) {
return this.users.get(id);
}
}
// ✅ 隐藏内部实现
class UserManager {
private users: Map<string, User>;
getUser(id: string): User | undefined {
return this.users.get(id);
}
addUser(user: User): void {
this.users.set(user.id, user);
}
}解决方案:
- 使用访问控制(private, protected)
- 提供明确的公共接口
- 隐藏实现细节
Symptoms: Abstraction exposes too many internal implementation details.
Example:
typescript
// ❌ 暴露内部状态
class UserManager {
public users: Map<string, User>; // 直接暴露内部数据结构
getUser(id: string) {
return this.users.get(id);
}
}
// ✅ 隐藏内部实现
class UserManager {
private users: Map<string, User>;
getUser(id: string): User | undefined {
return this.users.get(id);
}
addUser(user: User): void {
this.users.set(user.id, user);
}
}Solutions:
- Use access control (private, protected)
- Provide clear public interfaces
- Hide implementation details
最佳实践
Best Practices
1. 渐进式重构
1. Incremental Refactoring
不要试图一次性重构整个代码库:
策略:
- ✅ 每次重构一个小的重复区域
- ✅ 重构后立即测试
- ✅ 提交小的、原子性的变更
- ✅ 逐步扩大重构范围
示例工作流:
bash
undefinedDon't try to refactor the entire codebase at once:
Strategies:
- ✅ Refactor one small repetitive area at a time
- ✅ Test immediately after refactoring
- ✅ Commit small, atomic changes
- ✅ Gradually expand the refactoring scope
Example Workflow:
bash
undefined1. 创建特性分支
1. 创建特性分支
git checkout -b refactor/dry-pricing-logic
git checkout -b refactor/dry-pricing-logic
2. 重构一个模块
2. 重构一个模块
编辑 pricing_utils.js
编辑 pricing_utils.js
3. 测试
3. 测试
npm test
npm test
4. 提交
4. 提交
git add pricing_utils.js
git commit -m "Extract pricing calculation to reusable utility"
git add pricing_utils.js
git commit -m "Extract pricing calculation to reusable utility"
5. 重构使用该模块的文件
5. 重构使用该模块的文件
编辑 order_service.js
编辑 order_service.js
6. 再次测试和提交
6. 再次测试和提交
npm test
git add order_service.js
git commit -m "Refactor order service to use pricing utility"
npm test
git add order_service.js
git commit -m "Refactor order service to use pricing utility"
7. 继续其他模块...
7. 继续其他模块...
undefinedundefined2. 文档化你的抽象
2. Document Your Abstractions
好的抽象需要好的文档:
typescript
/**
* 计算商品折扣后的价格
*
* @param price - 原始价格(必须 >= 0)
* @param discountRate - 折扣率(0-1 之间,0.2 表示 20% 折扣)
* @returns 折扣后的价格
* @throws {Error} 如果 discountRate 不在有效范围内
*
* @example
* applyDiscount(100, 0.2) // 返回 80
* applyDiscount(50, 0) // 返回 50(无折扣)
*/
function applyDiscount(price: number, discountRate: number): number {
if (discountRate < 0 || discountRate > 1) {
throw new Error(`Invalid discount rate: ${discountRate}. Must be between 0 and 1.`);
}
return price * (1 - discountRate);
}Good abstractions require good documentation:
typescript
/**
* Calculates the discounted price of a product
*
* @param price - Original price (must be >= 0)
* @param discountRate - Discount rate (between 0 and 1, 0.2 means 20% off)
* @returns Discounted price
* @throws {Error} If discountRate is not within the valid range
*
* @example
* applyDiscount(100, 0.2) // Returns 80
* applyDiscount(50, 0) // Returns 50 (no discount)
*/
function applyDiscount(price: number, discountRate: number): number {
if (discountRate < 0 || discountRate > 1) {
throw new Error(`Invalid discount rate: ${discountRate}. Must be between 0 and 1.`);
}
return price * (1 - discountRate);
}3. 使用类型系统
3. Use Type Systems
利用类型系统防止误用:
typescript
// 使用类型别名增强可读性
type DiscountRate = number; // 0-1 之间
type Price = number; // >= 0
// 更好:使用品牌类型确保类型安全
type DiscountRate = number & { __brand: 'DiscountRate' };
function createDiscountRate(value: number): DiscountRate {
if (value < 0 || value > 1) {
throw new Error('Discount rate must be between 0 and 1');
}
return value as DiscountRate;
}
function applyDiscount(price: Price, discountRate: DiscountRate): Price {
return (price * (1 - discountRate)) as Price;
}
// 使用
const rate = createDiscountRate(0.2); // 类型检查通过
applyDiscount(100, rate);
// applyDiscount(100, 0.2); // ❌ 类型错误!必须使用 createDiscountRateLeverage type systems to prevent misuse:
typescript
// 使用类型别名增强可读性
type DiscountRate = number; // Between 0 and 1
type Price = number; // >= 0
// Better: Use branded types to ensure type safety
type DiscountRate = number & { __brand: 'DiscountRate' };
function createDiscountRate(value: number): DiscountRate {
if (value < 0 || value > 1) {
throw new Error('Discount rate must be between 0 and 1');
}
return value as DiscountRate;
}
function applyDiscount(price: Price, discountRate: DiscountRate): Price {
return (price * (1 - discountRate)) as Price;
}
// 使用
const rate = createDiscountRate(0.2); // Type check passes
applyDiscount(100, rate);
// applyDiscount(100, 0.2); // ❌ Type error! Must use createDiscountRate4. 重构前先写测试
4. Write Tests Before Refactoring
如果没有测试,先写测试再重构:
javascript
// 步骤 1:为现有(重复的)代码写测试
describe('Original Implementation', () => {
it('should calculate order total correctly', () => {
const order = {
items: [{ price: 10, quantity: 2 }],
memberLevel: 'gold'
};
expect(calculateOrderTotal(order)).toBe(16); // 20 * 0.8
});
});
// 步骤 2:重构代码
// 步骤 3:确保测试仍然通过
// npm testIf there are no tests, write tests first before refactoring:
javascript
// Step 1: Write tests for existing (duplicate) code
describe('Original Implementation', () => {
it('should calculate order total correctly', () => {
const order = {
items: [{ price: 10, quantity: 2 }],
memberLevel: 'gold'
};
expect(calculateOrderTotal(order)).toBe(16); // 20 * 0.8
});
});
// Step 2: Refactor the code
// Step 3: Ensure tests still pass
// npm test检查清单
Checklist
在完成 DRY 重构后,验证以下内容:
After completing DRY refactoring, verify the following:
识别阶段
Identification Phase
- 找到了所有明显的代码重复
- 识别了魔术数字和硬编码字符串
- 发现了结构性和逻辑性重复
- 记录了所有重复出现的位置
- Found all obvious code repetitions
- Identified magic numbers and hard-coded strings
- Discovered structural and logical repetitions
- Recorded all locations where repetitions occur
抽象阶段
Abstraction Phase
- 清楚区分了可变和不变部分
- 选择了合适的抽象形式(函数/类/配置)
- 抽象有清晰、描述性的命名
- 参数数量合理(≤ 4 个)
- 没有过度抽象
- Clearly distinguished between variable and invariant parts
- Chose the appropriate abstraction form (function/class/configuration)
- Abstractions have clear, descriptive names
- Number of parameters is reasonable (≤ 4)
- No over-abstraction
替换阶段
Replacement Phase
- 所有重复点都已替换
- 没有遗留旧代码
- 导入和依赖关系正确
- 处理了所有边缘情况
- All repetition points have been replaced
- No old code left
- Imports and dependencies are correct
- All edge cases are handled
验证阶段
Verification Phase
- 编写了单元测试
- 所有现有测试仍然通过
- 进行了手动验证(如适用)
- 性能没有显著下降
- 代码审查已完成(如在团队中工作)
- Written unit tests
- All existing tests still pass
- Performed manual verification (if applicable)
- Performance has not significantly degraded
- Code review completed (if working in a team)
整体质量
Overall Quality
- 代码更易读、易维护
- 单一职责原则得到遵守
- 修改只需在一个地方进行
- 有充分的文档和注释
- Code is more readable and maintainable
- Single Responsibility Principle is followed
- Modifications only need to be made in one place
- Sufficient documentation and comments
总结
Summary
DRY 原则是软件工程的基石之一。通过系统性地应用这个四步流程,你可以:
- 识别重复:培养对代码坏味道的敏感度
- 抽象逻辑:创建可复用、易维护的代码单元
- 替换实现:消除重复,统一实现
- 验证测试:确保重构的安全性
记住:
- 不要过早抽象(等到有明确重复时再抽象)
- 不要过度抽象(保持简单明了)
- 小步前进(渐进式重构比一次性大重构更安全)
- 测试是你的安全网(重构前先写测试)
最终目标:
让每一处知识在系统中都有唯一的、权威的表示。当需要修改时,你只改一个地方,所有使用该知识的地方自动更新。
这就是 DRY 的力量。
The DRY principle is one of the cornerstones of software engineering. By systematically applying this 4-step process, you can:
- Identify Repetition: Cultivate sensitivity to code smells
- Abstract Logic: Create reusable, maintainable code units
- Replace Implementation: Eliminate repetition and unify implementations
- Verify and Test: Ensure the safety of refactoring
Remember:
- Don't abstract prematurely (wait until there is clear repetition)
- Don't over-abstract (keep it simple and clear)
- Move forward in small steps (incremental refactoring is safer than one-time large-scale refactoring)
- Tests are your safety net (write tests before refactoring)
Final Goal:
Let every piece of knowledge have a unique, authoritative representation in the system. When modifications are needed, you only change one place, and all places using this knowledge are automatically updated.
This is the power of DRY.