修复糟糕的代码气味

简介: 修复糟糕的代码气味

原文链接:https://www.arjancodes.com/blog/best-practices-for-eliminating-python-code-smells/

文章列举了多种糟糕的代码模式,并给出了解决方法。通过这些修改,可以使得代码更易读、更可维护。

这些糟糕的代码气味是:

  1. 万能对象:一个类具有太多的功能,违背了单一责任原则。这个类会变得复杂,难以测试和维护。 解决方法:根据任务拆分成多个类。
  2. 重复代码:相同的代码块多次出现,增加了冗余,并且增加维护难度。 解决方法:抽象出一个函数,通过调用函数替代多个相同的代码块。
  3. 过长的方法:一个方法太长,说明这个方法做了太多事情,理解和维护该方法会很困难。 解决方法: 按照功能,拆分成若干的方法。
  4. 神奇数字: 代码中出现的神秘数字难以理解和修改。解决方法:定义一个常量表示数字的含义。
  5. 嵌套过深:过多的嵌套使得函数的流程难以把握。 解决办法: 去掉嵌套条件,必要时创建函数。

1. The “god object” smell (万能对象)

class OnlineStore:
    def search_product(self, query: Query):
        # Logic to search for products in some database
        pass
    def process_order(self, order: Order):
        # Logic to process the order and send confirmation email
        pass
    def handle_payment(self, payment_info: PaymentInfo):
        # Logic to handle payment and update the order status
        pass
    def manage_inventory(self, product_id: int, quantity: int):
        # Logic to manage inventory and update the database
        pass
    # Many more methods

“上帝对象”是整体设计的,处理了太多的任务和责任,违反了SOLID设计的单一责任原则(SRP, Single Responsibility priciple)。代码示例中的 OnlineStore类负责库存管理、订单处理、付款接受和产品搜索。将所有这些职责合并到一个类别中可能会限制我们引入新功能的灵活性,同时增加测试和维护的复杂性。

我们可以将 OnlineStore 类重写为更易于管理的专用类(如 ProductSearch 、 OrderProcessor 、 PaymentGateway 和 InventoryManager )。这使得每个类在遵守 SRP 的同时专注于特定任务。

class ProductSearch:
    def search_product(self, query: Query):
        # Logic to search for products in some database
        pass

class OrderProcessor:
    def process_order(self, order: Order):
        # Logic to process the order and send confirmation email
        pass

class PaymentGateway:
    def handle_payment(self, total: int, payment_info: Payment):
        # Logic to handle payment and update the order status
        pass

class InventoryManager:
    def manage_inventory(self, product_id: int, quantity: int):
        # Logic to manage inventory and update the database
        pass

2. The “duplicate code” smell (重复代码)

class ReportGenerator:
    def generate_sales_report(self, sales_data: list[Report):
        # Preprocessing steps, such as formatting the data into a table.
        # Generate sales report
        pass

    def generate_inventory_report(self, inventory_data: list[Report]):
        # Preprocessing steps (duplicated)
        # Generate inventory report
        pass

当相同的代码块多次出现时,它被视为重复代码。重复代码增加了冗余和不一致的可能。

我们可以将这些重复的过程组合成一个单一的方法来解决这个问题。通过这种方式,我们消除了冗余,并将其与 DRY(不要重复自己)编码理念保持一致。

class ReportGenerator:
    def preprocess_data(self, data: list[Report]):
        # Common preprocessing steps
        pass

    def generate_sales_report(self, sales_data: list[Report]):
        self.preprocess_data(sales_data)
        # Generate sales report
        pass

    def generate_inventory_report(self, inventory_data: list[Report]):
        self.preprocess_data(inventory_data)
        # Generate inventory report
        pass


3. The “long method” smell (方法太长)

def handle_customer_request(request: CustomerRequest):
    # Validate request
    # Log request details
    # Check inventory
    # Calculate pricing
    # Apply discounts
    # Finalize response
    pass

“长方法”包含太多的代码行,并且通常难以阅读、理解和测试。

通过将此方法分解为更小、更集中的函数,可以提高可读性和可重用性。通过将方法分离成更小、更集中的函数,我们可以提高可读性和可重用性,并简化单元测试。我们应该致力于使每种方法都负责一项单一的任务。

def handle_customer_request(request: CustomerRequest):
    validate_request(request)
    log_request(request)
    check_inventory(request)
    pricing = calculate_pricing(request)
    apply_discounts(pricing)
    return finalize_response(pricing)

def validate_request(request: Request): pass
def log_request(request: Request): pass
def check_inventory(request: Request): pass
def calculate_pricing(request: Request): pass
def apply_discounts(pricing: int): pass
def finalize_response(pricing: int): pass

4. The “magic numbers” smell (神奇数字)

def calculate_shipping_cost(distance: float) -> float:
    return distance * 1.25  # What does 1.25 signify?

“幻数”是那些棘手的数字文字,经常出现在编程代码中,没有明显的解释,使代码更难理解和处理。该 calculate_shipping_cost 函数在没有任何上下文的情况下使用数字 1.25,让我们猜测它的目的和含义。

相反,我们可以引入一个名为 PER_MILE_SHIPPING_RATE 的常量,它清楚地表明 1.25 表示每英里的运输成本。这个简单的更改使我们的代码更易于理解,也简化了将来对此值的更改。

PER_MILE_SHIPPING_RATE = 1.25

def calculate_shipping_cost(distance: float) -> float:
    return distance * PER_MILE_SHIPPING_RATE

5. The “nested conditionals” smell(嵌套过深)

def approve_loan(application: LoanApplication) -> bool:
    if application.credit_score > 600:
        if application.income > 30000:
            if application.debt_to_income_ratio < 0.4:
                return True
            else:
                return False
        else:
            return False
    else:
        return False

嵌套的条件语句可能会使理解函数的流变得困难。该 approve_loan 方法被一系列难以理解的嵌套 if 语句包围。

通过重构我们的代码,以便按顺序检查每个条件,我们可以创建一个更扁平、更易于阅读和理解的结构。如果将复杂的逻辑与条件混合在一起,则可能值得将逻辑抽象为单独的函数,以使条件更易于阅读。如果您有一系列需要满足的条件,请考虑使用 any 和 all 内置函数来使条件更具可读性。

def approve_loan(application: LoanApplication) -> bool:
    if application.credit_score <= 600:
        return False
    if application.income <= 30000:
        return False
    if application.debt_to_income_ratio >= 0.4:
        return False
    return True
def approve_loan(application: LoanApplication) -> bool:
    return all([
        application.credit_score > 600,
        application.income > 30000,
        application.debt_to_income_ratio < 0.4
    ])
相关文章
|
7月前
|
测试技术
无法复现的bug,如何处理?
无法复现的bug,如何处理?
528 0
|
7月前
在代码优化过程中,常见的错误和bug包括以下几点
在代码优化过程中,常见的错误和bug包括以下几点
|
小程序 Android开发 iOS开发
小程序 | 小程序修复了一些bug
前段时间,有朋友反应小程序的今天吃个啥有bug,不能正常使用。
144 0
|
测试技术
软件测试面试题:软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?
软件测试面试题:软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?
348 0
|
Java 数据安全/隐私保护 测试技术
我修复的印象最深的一个bug,一个导致CPU和内存异常到无法响应的BUG
系统上线一段时间后,客户反映接口响应特别慢,甚至没有响应,第一时间依次检查了网络、服务器资源使用情况,发现服务器CPU和内存占用率都非常的高,经过一阵紧张的排查,最终发现问题出现的根源,这就是我修复的印象最深的一个bug就是由于String的用法不当所造成的。
490 0
我修复的印象最深的一个bug,一个导致CPU和内存异常到无法响应的BUG
|
运维 Cloud Native 测试技术
高质量的缺陷分析:让自己少写 bug
缺陷分析做得好,bug 写得少。阿里资深技术专家和你分享如何进行高质量的缺陷分析,总结了 5 个要点,通过缺陷分析消除开发中的各种盲点,打造一个学习型的团队。
高质量的缺陷分析:让自己少写 bug
|
搜索推荐 IDE 测试技术
如何验证程序是否完成,测试以及修正Bug?
在日常中,我们码代码都是按照需求来的,为了验证我们的工作成果是否符合项目的需求,那么验证程序是否完成、测试以及修复bug就成了我们工作中非常重要的流程。
|
数据可视化 Java 程序员
有时候,解决问题比写代码更重要!
当你手里有把锤子的时候,看所有的东西都是钉子。 有时候程序员往往会陷入为了写代码而写代码的怪圈,没有意识到代码是为了解决现实问题的。当问题有更简便的解决方案时,写代码未必就是必须。
910 0