代码重构的案例分析

魔法少女 2022-02-16 ⋅ 11 阅读

在软件开发过程中,随着需求的变化和代码的不断演化,原有的代码可能会出现可读性差、可维护性低等问题。为了改善代码的质量,提高开发效率,我们可以采取代码重构的方式。本篇博客将通过一个实际的案例来分析代码重构的过程和效果。

问题描述

我们的案例是一个电商网站的购物车功能。初始版本的代码如下:

class Cart:
    def __init__(self):
        self.items = []

    def add_item(self, item):
        self.items.append(item)

    def remove_item(self, item):
        if item in self.items:
            self.items.remove(item)

    def calculate_total(self):
        total = 0
        for item in self.items:
            total += item.price * item.quantity
        return total

    def generate_invoice(self):
        invoice = ""
        for item in self.items:
            invoice += f"{item.name}: ${item.price * item.quantity}\n"
        return invoice

该版本的代码实现了基本的购物车功能,但存在以下问题:

  1. add_itemremove_item 方法没有进行输入参数的验证,可能导致传入非法值。
  2. calculate_totalgenerate_invoice 方法实现逻辑重复,违反了DRY原则(Don't Repeat Yourself)。
  3. calculate_totalgenerate_invoice 方法紧耦合,难以独立修改。

重构过程

在进行代码重构之前,我们需要先编写一组测试用例来保证重构之后的代码功能正常。以 pytest 为例,测试用例如下:

def test_cart():
    cart = Cart()

    # Add items to cart
    item1 = Item("Item 1", 10, 2)
    item2 = Item("Item 2", 20, 1)
    cart.add_item(item1)
    cart.add_item(item2)

    # Calculate total
    assert cart.calculate_total() == 40

    # Remove item from cart
    cart.remove_item(item1)
    assert cart.calculate_total() == 20

    # Generate invoice
    invoice = cart.generate_invoice()
    assert invoice == "Item 2: $20\n"

接下来,我们将使用一系列重构手法来改善代码质量。

  1. 提炼方法(Extract Method)

首先,我们将 calculate_totalgenerate_invoice 方法中重复的逻辑提炼到一个新的私有方法中,以遵循DRY原则。

def calculate_total(self):
    total = self._calculate_subtotal()
    return total

def generate_invoice(self):
    invoice = self._generate_item_details()
    return invoice

def _calculate_subtotal(self):
    subtotal = 0
    for item in self.items:
        subtotal += item.price * item.quantity
    return subtotal

def _generate_item_details(self):
    details = ""
    for item in self.items:
        details += f"{item.name}: ${item.price * item.quantity}\n"
    return details
  1. 提炼类(Extract Class)

接下来,我们将购物车中管理物品的逻辑提炼到一个新的类中,以实现功能聚焦和单一职责原则。

class CartItem:
    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

class Cart:
    def __init__(self):
        self.items = []

    def add_item(self, item):
        self.items.append(item)

    def remove_item(self, item):
        if item in self.items:
            self.items.remove(item)

    def calculate_total(self):
        total = self._calculate_subtotal()
        return total

    def generate_invoice(self):
        invoice = self._generate_item_details()
        return invoice

    def _calculate_subtotal(self):
        subtotal = 0
        for item in self.items:
            subtotal += item.price * item.quantity
        return subtotal

    def _generate_item_details(self):
        details = ""
        for item in self.items:
            details += f"{item.name}: ${item.price * item.quantity}\n"
        return details
  1. 引入参数对象(Introduce Parameter Object)

为了简化 add_itemremove_item 方法中的参数列表,我们可以引入一个参数对象来代表物品。

class CartItem:
    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

class Cart:
    def __init__(self):
        self.items = []

    def add_item(self, item):
        self.items.append(item)

    def remove_item(self, item):
        if item in self.items:
            self.items.remove(item)

    def calculate_total(self):
        total = self._calculate_subtotal()
        return total

    def generate_invoice(self):
        invoice = self._generate_item_details()
        return invoice

    def _calculate_subtotal(self):
        subtotal = 0
        for item in self.items:
            subtotal += item.price * item.quantity
        return subtotal

    def _generate_item_details(self):
        details = ""
        for item in self.items:
            details += f"{item.name}: ${item.price * item.quantity}\n"
        return details
  1. 封装集合(Encapsulate Collection)

为了更好地控制购物车中的物品集合,我们可以将 items 属性改为私有属性,并提供相应的访问方法。

class Cart:
    def __init__(self):
        self._items = []

    def add_item(self, item):
        self._items.append(item)

    def remove_item(self, item):
        if item in self._items:
            self._items.remove(item)

    def get_items(self):
        return self._items

    def calculate_total(self):
        total = self._calculate_subtotal()
        return total

    def generate_invoice(self):
        invoice = self._generate_item_details()
        return invoice

    def _calculate_subtotal(self):
        subtotal = 0
        for item in self._items:
            subtotal += item.price * item.quantity
        return subtotal

    def _generate_item_details(self):
        details = ""
        for item in self._items:
            details += f"{item.name}: ${item.price * item.quantity}\n"
        return details
  1. 提炼超类(Extract Superclass)

如果我们的购物车类需要与其他类型的购物车(如清单购物车、礼品购物车)进行共享,我们可以提炼一个超类来实现通用的购物车功能。

由于篇幅限制,本文不再演示该步骤的重构过程。

重构效果

经过以上一系列代码重构,我们成功地改进了购物车代码的结构和质量,并且提高了可读性和可维护性。重构后的代码更加易于扩展和修改,也更加符合面向对象的设计原则。

此外,通过编写测试用例,我们可以确保重构后的代码在功能上与原代码保持一致,从而降低了引入新错误的风险。

总结

通过以上案例分析,我们了解了代码重构的过程和效果。重构是一项常见的工作,通过不断改善代码质量,我们可以提高软件开发的效率和代码的可维护性。

代码重构并非一蹴而就的过程,需要根据具体的情况灵活选择合适的重构手法,并进行适度的测试来验证重构后的代码的正确性。

希望本篇博客对你了解代码重构有所帮助,同时也鼓励你在日常的开发工作中注重代码质量和可维护性,不断优化和改进自己的代码。


全部评论: 0

    我有话说: