程序员进阶工程师必备技能之代码质量与重构能力(三)

简介: 教程来源 https://wkmsa.cn/ 本文系统讲解重构的时机与方法:识别重复代码、过长函数等9种“代码坏味”;详解提取方法、内联方法、移动方法、多态替代条件、空对象等经典手法;并提供六步重构流程与渐进式实战示例,助你安全、高效提升代码质量。

三、重构的时机与方法

3.1 什么时候应该重构?
重构的信号(代码坏味):

重复代码:相同的代码出现在多个地方

过长函数:一个函数超过50行

过大类:一个类做了太多事情

过长参数列表:超过3个参数

发散式变化:一个类因为多种原因被修改

霰弹式修改:一个变化需要修改多个类

依恋情结:一个函数过度使用其他类的数据

数据泥团:多个数据项总是同时出现

基本类型偏执:使用基本类型代替小对象

switch/if链:大量的条件判断

# 坏味示例1:重复代码
# ❌ 重复代码
def process_order(order):
    total = 0
    for item in order.items:
        total += item.price * item.quantity
    if total > 100:
        total = total * 0.9
    return total

def process_cart(cart):
    total = 0
    for item in cart.items:
        total += item.price * item.quantity
    if total > 100:
        total = total * 0.9
    return total

# ✅ 提取公共方法
def calculate_total_with_discount(items: List[Item]) -> Decimal:
    total = sum(item.price * item.quantity for item in items)
    if total > 100:
        total = total * 0.9
    return total

def process_order(order):
    return calculate_total_with_discount(order.items)

def process_cart(cart):
    return calculate_total_with_discount(cart.items)


# 坏味示例2:过长参数列表
# ❌ 过长参数
def create_user(name, email, phone, address, city, state, zip_code, country):
    pass

# ✅ 引入参数对象
@dataclass
class UserInfo:
    name: str
    email: str
    phone: str
    address: Address

@dataclass
class Address:
    street: str
    city: str
    state: str
    zip_code: str
    country: str

def create_user(user_info: UserInfo):
    pass


# 坏味示例3:switch/if链
# ❌ 大量if-else
def calculate_discount(user_type, amount):
    if user_type == "normal":
        if amount > 100:
            return amount * 0.05
        else:
            return 0
    elif user_type == "vip":
        if amount > 100:
            return amount * 0.15
        elif amount > 50:
            return amount * 0.1
        else:
            return amount * 0.05
    elif user_type == "svip":
        return amount * 0.25
    elif user_type == "employee":
        return amount * 0.3
    else:
        return 0

# ✅ 使用策略模式+规则引擎
from abc import ABC, abstractmethod
from typing import List

class DiscountRule(ABC):
    @abstractmethod
    def calculate(self, amount: Decimal) -> Decimal:
        pass

class NormalUserDiscount(DiscountRule):
    def calculate(self, amount: Decimal) -> Decimal:
        return amount * Decimal('0.05') if amount > 100 else Decimal('0')

class VIPUserDiscount(DiscountRule):
    def calculate(self, amount: Decimal) -> Decimal:
        if amount > 100:
            return amount * Decimal('0.15')
        elif amount > 50:
            return amount * Decimal('0.1')
        return amount * Decimal('0.05')

class SVIPUserDiscount(DiscountRule):
    def calculate(self, amount: Decimal) -> Decimal:
        return amount * Decimal('0.25')

class EmployeeDiscount(DiscountRule):
    def calculate(self, amount: Decimal) -> Decimal:
        return amount * Decimal('0.3')

class DiscountCalculator:
    _rules = {
        "normal": NormalUserDiscount(),
        "vip": VIPUserDiscount(),
        "svip": SVIPUserDiscount(),
        "employee": EmployeeDiscount()
    }

    @classmethod
    def calculate(cls, user_type: str, amount: Decimal) -> Decimal:
        rule = cls._rules.get(user_type)
        if not rule:
            return Decimal('0')
        return rule.calculate(amount)

3.2 经典重构手法
3.2.1 提取方法(Extract Method)

# 重构前
def print_bill(customer):
    print(f"Customer: {customer.name}")
    print(f"Date: {datetime.now()}")

    total = 0
    for item in customer.orders:
        print(f"  {item.name}: ${item.price}")
        total += item.price

    print(f"Total: ${total}")

    if total > 100:
        discount = total * 0.1
        print(f"Discount: ${discount}")
        print(f"Final: ${total - discount}")

# 重构后
def print_bill(customer):
    print_header(customer)
    print_items(customer.orders)
    print_summary(customer.orders)

def print_header(customer):
    print(f"Customer: {customer.name}")
    print(f"Date: {datetime.now()}")

def print_items(orders):
    for item in orders:
        print(f"  {item.name}: ${item.price}")

def print_summary(orders):
    total = sum(item.price for item in orders)
    print(f"Total: ${total}")

    if total > 100:
        discount = total * 0.1
        print(f"Discount: ${discount}")
        print(f"Final: ${total - discount}")

3.2.2 内联方法(Inline Method)

# 重构前
def get_rating(driver):
    return more_than_five_late_deliveries(driver) ? 2 : 1

def more_than_five_late_deliveries(driver):
    return driver.number_of_late_deliveries > 5

# 重构后(函数太简单,直接内联)
def get_rating(driver):
    return 2 if driver.number_of_late_deliveries > 5 else 1

3.2.3 移动方法(Move Method)

# 重构前 - 方法放错了地方
class Account:
    def __init__(self, account_type, days_overdrawn):
        self.type = account_type
        self.days_overdrawn = days_overdrawn

    def bank_charge(self):
        result = 4.5
        if self.days_overdrawn > 0:
            result += self.overdraft_charge()
        return result

    def overdraft_charge(self):
        if self.type.is_premium:
            base_charge = 10
            if self.days_overdrawn <= 7:
                return base_charge
            else:
                return base_charge + (self.days_overdrawn - 7) * 0.85
        else:
            return self.days_overdrawn * 1.75

# 重构后 - 移动到合适的类
class AccountType:
    def overdraft_charge(self, days_overdrawn):
        if self.is_premium:
            base_charge = 10
            if days_overdrawn <= 7:
                return base_charge
            else:
                return base_charge + (days_overdrawn - 7) * 0.85
        else:
            return days_overdrawn * 1.75

class Account:
    def __init__(self, account_type, days_overdrawn):
        self.type = account_type
        self.days_overdrawn = days_overdrawn

    def bank_charge(self):
        result = 4.5
        if self.days_overdrawn > 0:
            result += self.type.overdraft_charge(self.days_overdrawn)
        return result

3.2.4 以多态取代条件表达式(Replace Conditional with Polymorphism)

# 重构前
class Bird:
    def __init__(self, name, type, number_of_coconuts=0, voltage=0, is_nailed=False):
        self.name = name
        self.type = type
        self.number_of_coconuts = number_of_coconuts
        self.voltage = voltage
        self.is_nailed = is_nailed

    def plumage(self):
        if self.type == "EuropeanSwallow":
            return "average"
        elif self.type == "AfricanSwallow":
            if self.number_of_coconuts > 2:
                return "tired"
            else:
                return "average"
        elif self.type == "NorwegianBlueParrot":
            if self.voltage > 100:
                return "scorched"
            else:
                return "beautiful"
        else:
            return "unknown"

    def air_speed_velocity(self):
        if self.type == "EuropeanSwallow":
            return 35
        elif self.type == "AfricanSwallow":
            return 40 - 2 * self.number_of_coconuts
        elif self.type == "NorwegianBlueParrot":
            return 0 if self.is_nailed else 10 + self.voltage / 10
        else:
            return None

# 重构后
from abc import ABC, abstractmethod

class Bird(ABC):
    def __init__(self, name):
        self.name = name

    @abstractmethod
    def plumage(self):
        pass

    @abstractmethod
    def air_speed_velocity(self):
        pass

class EuropeanSwallow(Bird):
    def plumage(self):
        return "average"

    def air_speed_velocity(self):
        return 35

class AfricanSwallow(Bird):
    def __init__(self, name, number_of_coconuts):
        super().__init__(name)
        self.number_of_coconuts = number_of_coconuts

    def plumage(self):
        return "tired" if self.number_of_coconuts > 2 else "average"

    def air_speed_velocity(self):
        return 40 - 2 * self.number_of_coconuts

class NorwegianBlueParrot(Bird):
    def __init__(self, name, voltage, is_nailed):
        super().__init__(name)
        self.voltage = voltage
        self.is_nailed = is_nailed

    def plumage(self):
        return "scorched" if self.voltage > 100 else "beautiful"

    def air_speed_velocity(self):
        return 0 if self.is_nailed else 10 + self.voltage / 10

# 工厂方法创建鸟对象
def create_bird(bird_data):
    if bird_data["type"] == "EuropeanSwallow":
        return EuropeanSwallow(bird_data["name"])
    elif bird_data["type"] == "AfricanSwallow":
        return AfricanSwallow(bird_data["name"], bird_data["number_of_coconuts"])
    elif bird_data["type"] == "NorwegianBlueParrot":
        return NorwegianBlueParrot(bird_data["name"], bird_data["voltage"], bird_data["is_nailed"])
    else:
        raise ValueError(f"Unknown bird type: {bird_data['type']}")

3.2.5 引入空对象(Introduce Null Object)

# 重构前
class Customer:
    def get_name(self):
        return self.name

    def get_plan(self):
        return self.plan

def get_customer_name(customer):
    if customer is None:
        return "Unknown"
    return customer.get_name()

# 重构后
class NullCustomer(Customer):
    def get_name(self):
        return "Unknown"

    def get_plan(self):
        return NullPlan()

    def is_null(self):
        return True

class RealCustomer(Customer):
    def get_name(self):
        return self.name

    def get_plan(self):
        return self.plan

    def is_null(self):
        return False

def get_customer_name(customer):
    # 不需要判空,空对象自然返回"Unknown"
    return customer.get_name()

# 使用
customer = find_customer(id) or NullCustomer()
name = customer.get_name()  # 安全,不需要判空

3.3 重构的步骤与策略

# 重构的六步法

"""
第一步:确保有足够的测试覆盖
    - 重构前先写测试
    - 确保测试通过
    - 每次小的重构后运行测试

第二步:识别重构目标
    - 找出代码坏味
    - 确定重构手法
    - 评估重构收益

第三步:小步前进
    - 每次只做一个重构
    - 每步后运行测试
    - 提交到版本控制

第四步:持续验证
    - 运行所有测试
    - 检查性能影响
    - Code Review

第五步:清理临时变量
    - 删除无用代码
    - 更新注释
    - 格式化代码

第六步:提交并文档化
    - 清晰的commit message
    - 更新相关文档
    - 通知团队
"""

# 重构示例:逐步改进

# 起始代码(需要重构)
def calculate_order_total(order):
    total = 0
    for i in range(len(order['items'])):
        item = order['items'][i]
        if item['type'] == 'physical':
            price = item['price']
            quantity = item['quantity']
            tax = price * 0.1
            total += (price + tax) * quantity
        elif item['type'] == 'digital':
            price = item['price']
            quantity = item['quantity']
            total += price * quantity
        elif item['type'] == 'gift_card':
            price = item['price']
            total += price

    if order.get('coupon'):
        if order['coupon']['type'] == 'percentage':
            total = total * (1 - order['coupon']['value'] / 100)
        elif order['coupon']['type'] == 'fixed':
            total = total - order['coupon']['value']

    if total < 0:
        total = 0

    return total

# 第一步:提取商品价格计算逻辑
def calculate_item_total(item):
    if item['type'] == 'physical':
        price = item['price']
        quantity = item['quantity']
        tax = price * 0.1
        return (price + tax) * quantity
    elif item['type'] == 'digital':
        return item['price'] * item['quantity']
    elif item['type'] == 'gift_card':
        return item['price']
    else:
        return 0

def calculate_order_total(order):
    total = sum(calculate_item_total(item) for item in order['items'])

    if order.get('coupon'):
        total = apply_coupon(total, order['coupon'])

    return max(total, 0)

# 第二步:使用多态替代类型判断
from abc import ABC, abstractmethod

class Item(ABC):
    def __init__(self, price, quantity):
        self.price = price
        self.quantity = quantity

    @abstractmethod
    def calculate_total(self):
        pass

class PhysicalItem(Item):
    def calculate_total(self):
        tax = self.price * 0.1
        return (self.price + tax) * self.quantity

class DigitalItem(Item):
    def calculate_total(self):
        return self.price * self.quantity

class GiftCardItem(Item):
    def calculate_total(self):
        return self.price

# 第三步:提取优惠计算策略
class CouponStrategy(ABC):
    @abstractmethod
    def apply(self, total):
        pass

class PercentageCoupon(CouponStrategy):
    def __init__(self, percentage):
        self.percentage = percentage

    def apply(self, total):
        return total * (1 - self.percentage / 100)

class FixedAmountCoupon(CouponStrategy):
    def __init__(self, amount):
        self.amount = amount

    def apply(self, total):
        return max(total - self.amount, 0)

def apply_coupon(total, coupon_data):
    if coupon_data['type'] == 'percentage':
        strategy = PercentageCoupon(coupon_data['value'])
    elif coupon_data['type'] == 'fixed':
        strategy = FixedAmountCoupon(coupon_data['value'])
    else:
        return total

    return strategy.apply(total)

# 最终重构后的代码
class OrderCalculator:
    def calculate_total(self, order):
        items_total = sum(item.calculate_total() for item in order.items)
        final_total = self._apply_coupon(items_total, order.coupon)
        return max(final_total, 0)

    def _apply_coupon(self, total, coupon):
        if not coupon:
            return total

        strategy = self._create_coupon_strategy(coupon)
        return strategy.apply(total)

    def _create_coupon_strategy(self, coupon):
        strategies = {
            'percentage': lambda c: PercentageCoupon(c['value']),
            'fixed': lambda c: FixedAmountCoupon(c['value'])
        }

        creator = strategies.get(coupon.type)
        if not creator:
            raise ValueError(f"Unknown coupon type: {coupon.type}")

        return creator(coupon)

来源:
https://aescc.cn/

相关文章
|
2月前
|
Kubernetes 安全 NoSQL
程序员进阶工程师必备技能之工程化与研发效率建设(四)
教程来源 https://bgnno.cn/ 该CI/CD流水线基于GitHub Actions构建:CI阶段涵盖代码规范检查(Black/Isort/Ruff/Mypy)、单元与集成测试(含PostgreSQL/Redis服务)、Docker镜像构建及Trivy安全扫描;CD阶段支持语义化版本触发部署,采用Kubernetes蓝绿发布策略,含人工审批、健康验证与自动回滚,兼顾安全性与可靠性。
|
3月前
|
算法 NoSQL Java
程序员必备的十大技能(进阶版)之高阶数据结构与算法(一)
教程来源 http://vbzcj.cn/ 本文系统讲解高阶数据结构与算法,涵盖复杂度精算(主定理、均摊分析)、跳表、并查集等高级线性结构,以及树、图、动态规划等核心内容,助力程序员突破性能瓶颈,实现工程级算法设计能力跃迁。
|
2月前
|
SQL 程序员 持续交付
程序员进阶工程师必备技能之代码质量与重构能力(四)
教程来源 https://ltglu.cn/ 本节系统介绍代码审查与质量保障实践,涵盖结构化审查清单、自动化检查(Ruff/MyPy/pytest)、CI质量门禁,以及从紧耦合遗留系统到领域驱动重构的完整实战案例,全面提升代码可读性、可维护性与安全性。
|
2月前
|
架构师 程序员 项目管理
程序员必备的十大技能(进阶版)之架构规划与项目统筹(一)
教程来源 http://fndvx.cn/ 本文系统解析架构规划与项目统筹能力,涵盖架构思维、风格选型、边界划分、非功能设计、技术决策、文档治理、全周期管理等十大维度,助你构建技术与管理兼备的复合能力。
|
2月前
|
程序员 Linux
程序员必备的十大技能(进阶版)之底层计算机原理(三)
教程来源 http://tmywi.cn/ 本节深入解析程序构建与系统底层机制:涵盖编译四阶段(预处理、编译、汇编、链接)、ELF文件结构及动态链接原理(PLT/GOT);并详解Linux进程实现(task_struct、fork/COW)、上下文切换开销、系统调用流程(syscall)与虚拟内存分页机制(四级页表)。
|
2月前
|
NoSQL 程序员 API
程序员进阶工程师必备技能之架构落地与组件封装(三)
教程来源 bhttps://bncne.cn/ 本文系统阐述微服务架构落地的最佳实践:涵盖分阶段实施策略(验证→基建→迭代)、自动化架构治理规则(分层/依赖/API/数据库),以及基于Redis的高可用服务注册发现组件实现,助力企业稳健完成架构升级。
|
2月前
|
程序员 数据库 数据安全/隐私保护
程序员进阶工程师必备技能之架构落地与组件封装(一)
教程来源 https://oplhc.cn/ 本文剖析程序员从“写代码”到“做架构”的关键跃迁,直击3–5年开发者面临的系统僵化困境。揭示架构本质是解决“如何组织”而非“如何实现”,详解组件封装、分层设计(四层/六边形/CQRS)、五大核心原则及落地实践,助你构建可维护、易扩展、高协作的复杂系统。
|
2月前
|
SQL 存储 关系型数据库
覆盖索引:让你的查询直接从索引返回,彻底告别回表
覆盖索引是SQL优化中性价比较高的技巧,让查询直接从索引返回所需列,避免回表操作。本文解释覆盖索引的原理,通过EXPLAIN的“Using index”判断是否生效。结合复合索引设计、深分页优化(延迟关联)等场景,给出覆盖索引的使用方法和注意事项。用好覆盖索引,不改SQL逻辑,仅调整索引设计即可显著提升查询性能。
|
2月前
|
人工智能 自然语言处理 安全
阿里云云部署OpenClaw集成钉钉
本文详解OpenClaw开源AI助手与钉钉的深度集成:支持群聊/单聊中自然语言交互,涵盖环境部署、钉钉应用创建、通道配置、机器人测试及多Agent绑定等全流程,并强调使用前须评估安全与合规性。
|
2月前
|
存储 消息中间件 SQL
Java在分布式链路追踪系统(Jaeger)中的实现与集成
微服务架构中,一个用户请求可能跨越多达几十个服务。当出现延迟增加或错误时,难以定位具体哪个服务出问题。
185 5