在软件开发过程中,随着需求的变化和代码的不断演化,原有的代码可能会出现可读性差、可维护性低等问题。为了改善代码的质量,提高开发效率,我们可以采取代码重构的方式。本篇博客将通过一个实际的案例来分析代码重构的过程和效果。
问题描述
我们的案例是一个电商网站的购物车功能。初始版本的代码如下:
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
该版本的代码实现了基本的购物车功能,但存在以下问题:
add_item
和remove_item
方法没有进行输入参数的验证,可能导致传入非法值。calculate_total
和generate_invoice
方法实现逻辑重复,违反了DRY原则(Don't Repeat Yourself)。calculate_total
和generate_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"
接下来,我们将使用一系列重构手法来改善代码质量。
- 提炼方法(Extract Method)
首先,我们将 calculate_total
和 generate_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
- 提炼类(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
- 引入参数对象(Introduce Parameter Object)
为了简化 add_item
和 remove_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
- 封装集合(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
- 提炼超类(Extract Superclass)
如果我们的购物车类需要与其他类型的购物车(如清单购物车、礼品购物车)进行共享,我们可以提炼一个超类来实现通用的购物车功能。
由于篇幅限制,本文不再演示该步骤的重构过程。
重构效果
经过以上一系列代码重构,我们成功地改进了购物车代码的结构和质量,并且提高了可读性和可维护性。重构后的代码更加易于扩展和修改,也更加符合面向对象的设计原则。
此外,通过编写测试用例,我们可以确保重构后的代码在功能上与原代码保持一致,从而降低了引入新错误的风险。
总结
通过以上案例分析,我们了解了代码重构的过程和效果。重构是一项常见的工作,通过不断改善代码质量,我们可以提高软件开发的效率和代码的可维护性。
代码重构并非一蹴而就的过程,需要根据具体的情况灵活选择合适的重构手法,并进行适度的测试来验证重构后的代码的正确性。
希望本篇博客对你了解代码重构有所帮助,同时也鼓励你在日常的开发工作中注重代码质量和可维护性,不断优化和改进自己的代码。