Loading...
Loading...
This skill guides systematic code refactoring following the DRY (Don't Repeat Yourself) principle. Use it when users request to eliminate code duplication, refactor repetitive code, apply the DRY principle, or mention code smells like copy-paste code, magic numbers, or repeated logic. It implements a 4-step workflow from identifying repetition to verified refactoring.
npx skill4agent add yyh211/claude-meta-skill dry-refactoringEvery piece of knowledge in a system must have a single, unambiguous, authoritative representation.
// 重复 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# 魔术数字重复
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// 结构性重复
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';
}
}// 逻辑重复:都在计算折扣,只是来源不同
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));
}// 原始重复代码
const userEmail = validateEmail(user.email);
const adminEmail = validateEmail(admin.email);
const supportEmail = validateEmail(support.email);
// 分析:
// 不变部分:validateEmail() 调用
// 可变部分:不同的 email 值| 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 |
# ❌ 重复代码
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
# 处理支付...// ❌ 魔术数字
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}`);
}// ❌ 重复的状态和行为
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>();# 位置 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
# 创建用户...
# 位置 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
# 创建产品...# 新抽象: 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
# 位置 1: user_service.py (已替换)
def create_user(data):
error = validate_required_fields(data, ['email', 'password'])
if error:
return error
# 创建用户...
# 位置 2: product_service.py (已替换)
def create_product(data):
error = validate_required_fields(data, ['name', 'price'])
if error:
return error
# 创建产品...// 大部分重复,但有一个地方需要额外日志
function processData(data, options = {}) {
// 通用处理...
if (options.enableLogging) {
console.log('Processing:', data);
}
return result;
}
// 使用
processData(data1); // 无日志
processData(data2, { enableLogging: true }); // 有日志// 核心流程相同,但中间步骤不同
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));# 如果某个重复有本质上的不同,考虑保留它
def process_standard_order(order):
return apply_dry_abstraction(order, 'standard')
def process_vip_order(order):
# VIP 订单有完全不同的业务逻辑,不强行抽象
# 保留独立实现
pass# 抽象函数
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)# 运行特定模块的测试
pytest tests/test_user_service.py
pytest tests/test_product_service.py
# 或运行整个测试套件
npm test
pytestimport time
# 性能测试
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}%")// 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;
}0.80.9// 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 };// 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);
}// 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.jsDISCOUNT_RATES// ❌ 过度抽象
function universalProcessor(data, options, callbacks, config, meta) {
// 100 行通用处理逻辑...
}
// ✅ 合理抽象
function processUserData(user) {
return validate(user) && transform(user);
}# ❌ 每次调用都重新编译正则表达式
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)// ❌ 暴露内部状态
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);
}
}# 1. 创建特性分支
git checkout -b refactor/dry-pricing-logic
# 2. 重构一个模块
# 编辑 pricing_utils.js
# 3. 测试
npm test
# 4. 提交
git add pricing_utils.js
git commit -m "Extract pricing calculation to reusable utility"
# 5. 重构使用该模块的文件
# 编辑 order_service.js
# 6. 再次测试和提交
npm test
git add order_service.js
git commit -m "Refactor order service to use pricing utility"
# 7. 继续其他模块.../**
* 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);
}// 使用类型别名增强可读性
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 createDiscountRate// 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 testLet 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.