你有没有过这样的经历?信心满满改了一段旧代码,结果上线后发现功能坏了——用户付款成功但没收到通知,或者订单金额算错了。这不是你技术差,是重构前没做一件关键的事:写测试。

先写测试,给重构系上“安全绳”
重构的本质是“改变代码结构而不改变功能”,所以必须先用测试覆盖原有逻辑。比如你要重构一个计算订单金额的函数,先写几个单元测试:
# 原有逻辑的单元测试(以Python+pytest为例)
def test_calculate_order_total_normal():
# 正常订单:2件商品,无折扣
items = [Item(name="T恤", price=100, quantity=2)]
order = Order(items=items, discount=0)
assert calculate_order_total(order) == 200
def test_calculate_order_total_with_discount():
# 有折扣的订单:2件商品,9折
items = [Item(name="T恤", price=100, quantity=2)]
order = Order(items=items, discount=0.1)
assert calculate_order_total(order) == 180
def test_calculate_order_total_empty_items():
# 空订单:抛出异常
with pytest.raises(ValueError):
order = Order(items=[], discount=0)
calculate_order_total(order)
这些测试就像“安全绳”——重构后跑一遍,如果所有测试都通过,说明你没改坏功能;如果失败,立刻能定位问题。记住:没有测试的重构,都是“裸奔”。
用“提炼函数”把长函数拆成“一口能咽下去的小蛋糕”
长函数是代码里最常见的“坏味道”。比如你写了一个处理用户订单的函数,里面又验证参数、又算金额、又存数据库、又发通知,足足有50行——过了一周再看,根本记不清哪部分是哪部分。
比如这段“混乱”的代码:
def process_order(order):
# 1. 验证订单参数
if not order.items:
raise ValueError("订单没有商品")
if not order.user_id:
raise ValueError("用户ID不能为空")
# 2. 计算订单总额
total = 0
for item in order.items:
total += item.price * item.quantity
total *= (1 - order.discount)
# 3. 保存订单记录
order_record = OrderRecord(
order_id=order.id,
user_id=order.user_id,
total_amount=total,
created_at=datetime.now()
)
db.session.add(order_record)
db.session.commit()
# 4. 发送通知邮件
send_email(
to=order.user_email,
subject="订单已处理",
body=f"您的订单金额为:{total}元"
)
看着累吗?我们用提炼函数把它拆成4个小函数,每个函数只做一件事:
def process_order(order):
validate_order_params(order) # 验证参数(职责1)
total = calculate_order_total(order) # 计算总额(职责2)
save_order_to_db(order, total) # 保存数据库(职责3)
send_order_notification(order, total) # 发送通知(职责4)
# 提炼出来的小函数,名字即功能
def validate_order_params(order):
if not order.items:
raise ValueError("订单没有商品")
if not order.user_id:
raise ValueError("用户ID不能为空")
def calculate_order_total(order):
total = sum(item.price * item.quantity for item in order.items)
return total * (1 - order.discount)
def save_order_to_db(order, total):
order_record = OrderRecord(
order_id=order.id,
user_id=order.user_id,
total_amount=total,
created_at=datetime.now()
)
db.session.add(order_record)
db.session.commit()
def send_order_notification(order, total):
send_email(
to=order.user_email,
subject="订单已处理",
body=f"您的订单金额为:{total}元"
)
改完之后,是不是一眼就能看懂process_order
做了什么?每个小函数都有清晰的名字,像“validate_order_params”“calculate_order_total”,不用看里面的代码就知道功能。以后要修改“发送通知”的逻辑,直接找send_order_notification
就行——这就是单一职责原则的落地。
用“替换魔法数”让代码“会说话”
你有没有见过这样的代码?
if order.status == 3:
send_shipping_notification(order)
“3”是什么意思?是“已支付”还是“已发货”?过了一个月,连写这段代码的你都得猜。这就是魔法数——隐晦的硬编码,让代码像“密码”一样难读。
解决办法很简单:把魔法数换成有意义的常量。比如:
# 定义常量(可以放在单独的constants.py文件里)
STATUS_UNPAID = 1 # 未支付
STATUS_PAID = 2 # 已支付
STATUS_SHIPPED = 3 # 已发货
STATUS_COMPLETED = 4 # 已完成
# 重构后的代码
if order.status == STATUS_SHIPPED:
send_shipping_notification(order)
这样改完,不管是你还是同事,看代码就能立刻明白:“哦,当订单状态是‘已发货’时,发送通知”。还有魔法字符串(比如if role == 'admin'
),换成常量ROLE_ADMIN
,效果一样。
用“分解条件表达式”把嵌套逻辑拆成“平的”
你有没有写过这样的代码?
def calculate_discount(user, order):
if user.is_vip:
if order.total > 1000:
return 0.15 # VIP且订单超过1000,打85折
else:
return 0.1 # VIP但订单不足1000,打9折
else:
if order.total > 500:
return 0.05 # 普通用户订单超过500,打95折
else:
return 0 # 普通用户订单不足500,无折扣
四层嵌套的if-else,像“千层饼”一样,看久了眼睛都花。我们可以用分解条件表达式,把每个条件分支拆成小函数:
def calculate_discount(user, order):
if is_vip_user(user):
return vip_discount(order.total)
else:
return regular_user_discount(order.total)
# 拆出来的小函数,每个只做一件事
def is_vip_user(user):
return user.is_vip
def vip_discount(total):
return 0.15 if total > 1000 else 0.1
def regular_user_discount(total):
return 0.05 if total > 500 else 0
这样改完,逻辑像“平铺的地板”一样清晰——即使过了半年,你也能立刻看懂折扣规则。下次遇到嵌套的if-else,记得问自己:“这个条件能不能拆成小函数?”
用“引入参数对象”终结“参数列表爆炸”
你有没有写过这样的函数?
def create_order(name, email, phone, address, product_id, quantity):
# 处理订单创建逻辑
6个参数,看着就头大——如果要加一个“性别”参数,还得改函数签名和所有调用的地方。这就是参数列表爆炸,解决办法是引入参数对象:把相关的参数打包成一个对象。
比如上面的参数中,name
、email
、phone
、address
都是用户信息,我们可以定义一个UserInfo
类:
# 定义参数对象
class UserInfo:
def __init__(self, name, email, phone, address):
self.name = name
self.email = email
self.phone = phone
self.address = address
# 重构后的函数,参数列表更简洁
def create_order(user_info, product_id, quantity):
# 处理订单创建逻辑
print(f"用户姓名:{user_info.name}")
print(f"用户邮箱:{user_info.email}")
这样参数列表从6个变成3个,更简洁;以后要加用户的“性别”参数,只需要改UserInfo
类,不用动create_order
的签名——是不是省心多了?
重构的“小技巧”:让你少踩坑
最后分享几个实战中总结的“避坑技巧”,都是血的教训:
- 小步重构,不要“爆改”:每天花15分钟改一小块(比如把一个长函数拆成两个,或者把一个魔法数换成常量)。积少成多,比周末花一天改1000行代码更安全——毕竟改得多,错得多。
- 重构后立刻提交代码:改完一小块,跑一遍测试,没问题就提交——万一改坏了,还能回滚到上一个版本。
- 不要为了“优雅”而重构:如果一段代码能满足需求,且没有影响可读性,就不用改。重构是“优化”,不是“炫技”。
- 和同事对齐规范:比如常量用大写,函数名用“动词+名词”(比如
calculate_total
),避免自己改的代码和团队风格冲突——毕竟代码是写给人看的,不是写给机器的。
如何识别“该重构了”的信号?
最后给你一份“重构信号清单”,遇到这些情况,说明代码该改了:
– 写代码时,你需要“绕着”旧代码写补丁(比如“这个函数太复杂,我不敢改,只能加个新函数”);
– 同事问你“这段代码是做什么的”,你得花5分钟才能解释清楚;
– 函数参数超过3个,或者if-else嵌套超过2层;
– 你改了一个bug,结果引出了3个新bug;
– 过了一周再看自己写的代码,根本记不清里面的逻辑。
这些信号不是“麻烦”,是代码在“喊救命”——赶紧动手重构吧!
原创文章,作者:,如若转载,请注明出处:https://zube.cn/archives/300