跳转至

438_代码重构方法论


一句话说明

重构是"不改变外部行为、改善内部结构"的过程,目标是让代码更易读、易扩展、易测试,核心是每次只做一个小改动,并保证测试通过。


核心知识点

白话理解

房子的格局不好用,重新装修但不改地基——这就是重构。代码重构不是推倒重写,而是一步一步把"房间布局"改得更合理,每改一步确保"住进去还是好的"(测试通过)。

常见重构手法(Martin Fowler 经典)

  1. 提炼函数(Extract Function):把一段逻辑提成单独函数,给它一个好名字
  2. 内联函数(Inline Function):太短的函数直接内联,减少跳转
  3. 移动函数(Move Function):函数放错了类/模块,移到合适的地方
  4. 以查询取代临时变量:临时变量换成方法调用,减少状态
  5. 引入参数对象:多个相关参数打包成一个对象
  6. 分解条件表达式:复杂 if-else 提成函数,提高可读性
  7. 以多态取代条件:大量 if-else/switch 用多态替代

经典题目与解法

题目1:重构一段"坏代码"——把大函数拆小

题意:以下函数做了太多事,请重构为多个职责单一的小函数。

# ===== 重构前(bad code)=====
def process_order(order):
    # 校验
    if order['amount'] <= 0:
        print("金额无效")
        return None
    if order['customer'] == '':
        print("客户名为空")
        return None

    # 计算折扣
    if order['amount'] > 1000:
        discount = 0.1
    elif order['amount'] > 500:
        discount = 0.05
    else:
        discount = 0

    # 计算最终价格
    final = order['amount'] * (1 - discount)

    # 打印收据
    print(f"客户: {order['customer']}")
    print(f"原价: {order['amount']}")
    print(f"折扣: {discount * 100}%")
    print(f"实付: {final}")
    return final


# ===== 重构后(clean code)=====
def validate_order(order) -> bool:
    """校验订单合法性"""
    if order['amount'] <= 0:
        print("金额无效")
        return False
    if not order['customer']:              # 空字符串判断更Pythonic
        print("客户名为空")
        return False
    return True

def calculate_discount(amount: float) -> float:
    """根据金额计算折扣率"""
    if amount > 1000:
        return 0.1                         # 大额客户10%折扣
    if amount > 500:
        return 0.05                        # 中额客户5%折扣
    return 0.0                             # 小额无折扣

def calculate_final_price(amount: float, discount: float) -> float:
    """计算折后价"""
    return amount * (1 - discount)

def print_receipt(order: dict, discount: float, final: float):
    """打印收据"""
    print(f"客户: {order['customer']}")
    print(f"原价: {order['amount']}")
    print(f"折扣: {discount * 100:.0f}%")
    print(f"实付: {final:.2f}")

def process_order(order: dict):
    """处理订单主流程——每个步骤委托给专职函数"""
    if not validate_order(order):          # 校验
        return None

    discount = calculate_discount(order['amount'])     # 计算折扣
    final = calculate_final_price(order['amount'], discount)  # 计算价格
    print_receipt(order, discount, final)              # 打印收据
    return final

# 测试
order = {'customer': '张三', 'amount': 1200}
process_order(order)

重构收益:每个函数职责单一,可独立测试,更易复用和修改


题目2:用多态替代 if-else(Strategy 模式)

题意:根据支付方式计算手续费,目前用 if-else,随着支付方式增多代码会膨胀,请重构。

# ===== 重构前(if-else膨胀)=====
def calculate_fee(payment_type: str, amount: float) -> float:
    if payment_type == 'credit_card':
        return amount * 0.02              # 信用卡2%
    elif payment_type == 'alipay':
        return amount * 0.006             # 支付宝0.6%
    elif payment_type == 'wechat':
        return amount * 0.006             # 微信0.6%
    elif payment_type == 'bank_transfer':
        return 5.0                        # 银行转账固定5元
    else:
        raise ValueError(f"未知支付方式: {payment_type}")


# ===== 重构后(多态替代条件)=====
from abc import ABC, abstractmethod

class PaymentMethod(ABC):
    """支付方式抽象基类"""
    @abstractmethod
    def calculate_fee(self, amount: float) -> float:
        pass

class CreditCard(PaymentMethod):
    def calculate_fee(self, amount: float) -> float:
        return amount * 0.02              # 信用卡2%手续费

class Alipay(PaymentMethod):
    def calculate_fee(self, amount: float) -> float:
        return amount * 0.006             # 支付宝0.6%

class WechatPay(PaymentMethod):
    def calculate_fee(self, amount: float) -> float:
        return amount * 0.006             # 微信支付0.6%

class BankTransfer(PaymentMethod):
    def calculate_fee(self, amount: float) -> float:
        return 5.0                        # 银行转账固定5元

# 注册表:新增支付方式只需加一行,不改 calculate_fee 主函数
PAYMENT_REGISTRY = {
    'credit_card': CreditCard(),
    'alipay': Alipay(),
    'wechat': WechatPay(),
    'bank_transfer': BankTransfer(),
}

def calculate_fee(payment_type: str, amount: float) -> float:
    """主函数:查注册表,委托给具体实现"""
    method = PAYMENT_REGISTRY.get(payment_type)
    if not method:
        raise ValueError(f"未知支付方式: {payment_type}")
    return method.calculate_fee(amount)  # 多态调用

# 测试
print(calculate_fee('credit_card', 1000))   # 20.0
print(calculate_fee('alipay', 1000))        # 6.0

时间复杂度:O(1)(字典查找)
重构收益:开闭原则——对扩展开放,对修改关闭


面试技巧

  1. 先测试再重构:没有测试保护的重构是"在走钢丝",先写测试再动代码
  2. 小步前进:每次重构只做一件事,改完运行测试,绿灯再下一步
  3. 命名是最重要的重构:好的函数名让代码自说明,减少注释需求
  4. 说清楚 Why:面试时不只展示 How,要解释"为什么这样改更好"
  5. 提示代码坏味道:长函数、重复代码、过长参数列表、散弹式修改——说出这些专业术语

速查表

坏味道重构手法效果
长函数提炼函数职责单一
大量if-else多态/策略模式开闭原则
重复代码提炼共用函数DRY原则
过长参数列表引入参数对象接口清晰
数据泥团提炼类高内聚
魔法数字提炼常量可读性
# 重构流程口诀
# 1. Red → Green → Refactor(TDD保驾护航)
# 2. 一次只做一件事
# 3. 改完必须跑测试
# 4. 提交小步 commit