写在前面
这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许优质的单元测试是一个切入点。 就我个人而言,这本书确实很有帮助。第一次读的时候,很多细节我都不太懂,但将书中内容应用到工作中后,我受益匪浅。比如面对一些让人抓狂的代码设计时,书里的方法能让我逐步深入理解代码的逻辑与设计。 作为一名测试开发工程师,我想把学习这本书的经验分享给大家,希望能给大家带来帮助。因为现在工作中大多使用 Python 代码,所以我把书中JAVA案例都用 Python 代码进行了改写 。
在测试驱动开发(TDD)的实践中,设计模式是解决软件开发常见问题、提升代码质量与可维护性的有力工具。以下详细介绍多种设计模式及其 Python 示例代码。
命令(Command)模式
概念与应用场景
当需调用复杂运算时,命令模式将请求封装为对象,实现发送与执行请求解耦,便于添加日志记录、撤销等功能。
示例代码
# 命令基类
class Command:
def execute(self):
pass
# 加法命令
class AddCommand(Command):
def __init__(self, calculator, operand):
self.calculator = calculator
self.operand = operand
def execute(self):
self.calculator.add(self.operand)
# 减法命令
class SubtractCommand(Command):
def __init__(self, calculator, operand):
self.calculator = calculator
self.operand = operand
def execute(self):
self.calculator.subtract(self.operand)
# 计算器类
class Calculator:
def __init__(self):
self.result = 0
def add(self, num):
self.result += num
def subtract(self, num):
self.result -= num
# 测试代码
import unittest
class TestCommandPattern(unittest.TestCase):
def test_command_pattern(self):
calculator = Calculator()
add_command = AddCommand(calculator, 5)
subtract_command = SubtractCommand(calculator, 3)
add_command.execute()
subtract_command.execute()
self.assertEqual(calculator.result, 2)
if __name__ == '__main__':
unittest.main()
值对象(Value Object)模式
概念与特性
值对象模式用于创建可广泛共享、身份不重要且状态不可变的对象,操作返回新对象,避免别名问题。
示例代码
# 货币值对象类
class Money:
def __init__(self, amount):
self.amount = amount
def add(self, other):
return Money(self.amount + other.amount)
def subtract(self, other):
return Money(self.amount - other.amount)
def __eq__(self, other):
return isinstance(other, Money) and self.amount == other.amount
def __hash__(self):
return hash(self.amount)
# 测试代码
import unittest
class TestValueObjectPattern(unittest.TestCase):
def test_value_object_pattern(self):
five = Money(5)
three = Money(3)
sum_result = five.add(three)
self.assertEqual(sum_result.amount, 8)
difference = five.subtract(three)
self.assertEqual(difference.amount, 2)
if __name__ == '__main__':
unittest.main()
Null 对象(Null Object)模式
概念与用途
Null 对象模式创建特定对象表示特殊情况,避免频繁空值检查,使代码简洁优雅。
示例代码
# 日志记录器接口
class Logger:
def log(self, message):
pass
# 实际的日志记录器
class ConsoleLogger(Logger):
def log(self, message):
print(f"Logging: {message}")
# Null 日志记录器
class NullLogger(Logger):
def log(self, message):
pass
# 使用日志记录器的类
class MyClass:
def __init__(self, logger):
self.logger = logger
def do_something(self):
self.logger.log("Doing something...")
# 测试代码
import unittest
class TestNullObjectPattern(unittest.TestCase):
def test_null_object_pattern(self):
with_console_logger = MyClass(ConsoleLogger())
with_console_logger.do_something()
with_null_logger = MyClass(NullLogger())
with_null_logger.do_something()
if __name__ == '__main__':
unittest.main()
模板方法(Template Method)模式
概念与结构
模板方法模式定义操作的算法骨架,将部分步骤延迟到子类实现,子类可在不改变算法结构时重定义某些步骤。
示例代码
import abc
# 测试用例模板类
class TestCase(metaclass=abc.ABCMeta):
def run_bare(self):
self.set_up()
try:
self.run_test()
finally:
self.tear_down()
@abc.abstractmethod
def set_up(self):
pass
@abc.abstractmethod
def run_test(self):
pass
@abc.abstractmethod
def tear_down(self):
pass
# 具体测试用例类
class MyTestCase(TestCase):
def __init__(self):
self.result = 0
def set_up(self):
self.result = 0
def run_test(self):
self.result = 5 + 3
def tear_down(self):
self.result = 0
# 测试代码
import unittest
class TestTemplateMethodPattern(unittest.TestCase):
def test_template_method_pattern(self):
test_case = MyTestCase()
test_case.run_bare()
self.assertEqual(test_case.result, 8)
if __name__ == '__main__':
unittest.main()
可插入对象(Pluggable Object)模式
概念与应用
可插入对象模式通过插入不同实现逻辑实现不同工作流,满足多样化需求。
示例代码
# 选择模式接口
class SelectionMode:
def select(self):
pass
def move(self):
pass
def unselect(self):
pass
# 单选模式
class SingleSelection(SelectionMode):
def select(self):
print("Single selection")
def move(self):
print("Move single selection")
def unselect(self):
print("Unselect single selection")
# 多选模式
class MultipleSelection(SelectionMode):
def select(self):
print("Multiple selection")
def move(self):
print("Move multiple selection")
def unselect(self):
print("Unselect multiple selection")
# 选择工具类
class SelectionTool:
def __init__(self, mode):
self.mode = mode
def mouse_down(self):
self.mode.select()
def mouse_move(self):
self.mode.move()
def mouse_up(self):
self.mode.unselect()
# 测试代码
import unittest
class TestPluggableObjectPattern(unittest.TestCase):
def test_pluggable_object_pattern(self):
single_tool = SelectionTool(SingleSelection())
single_tool.mouse_down()
single_tool.mouse_move()
single_tool.mouse_up()
multiple_tool = SelectionTool(MultipleSelection())
multiple_tool.mouse_down()
multiple_tool.mouse_move()
multiple_tool.mouse_up()
if __name__ == '__main__':
unittest.main()
可插入选择器(Pluggable Selector)模式
概念与实现方式
可插入选择器模式通过动态调用不同实例方法,避免过多子类和复杂条件分支,提升代码灵活性与可维护性。
示例代码
# 报表抽象类
class Report:
def __init__(self, print_message):
self.print_message = print_message
def print_report(self):
pass
# HTML 报表类
class HTMLReport(Report):
def print_report(self):
print(f"Printing HTML report: {self.print_message}")
# XML 报表类
class XMLReport(Report):
def print_report(self):
print(f"Printing XML report: {self.print_message}")
# 可插入选择器类
class ReportSelector:
def __init__(self, report):
self.report = report
def print(self):
self.report.print_report()
# 测试代码
import unittest
class TestPluggableSelectorPattern(unittest.TestCase):
def test_pluggable_selector_pattern(self):
html_report = HTMLReport("Sample HTML content")
html_selector = ReportSelector(html_report)
html_selector.print()
xml_report = XMLReport("Sample XML content")
xml_selector = ReportSelector(xml_report)
xml_selector.print()
if __name__ == '__main__':
unittest.main()
工厂方法(Factory Method)模式
概念与优势
工厂方法模式将对象创建逻辑封装在方法中,创建对象时可依条件返回不同类型对象,增强代码灵活性与扩展性。
示例代码
# 货币抽象类
class Money:
def __init__(self, amount):
self.amount = amount
def times(self, multiplier):
pass
# 美元类
class Dollar(Money):
def times(self, multiplier):
return Dollar(self.amount * multiplier)
# 货币工厂类
class MoneyFactory:
@staticmethod
def dollar(amount):
return Dollar(amount)
# 测试代码
import unittest
class TestFactoryMethodPattern(unittest.TestCase):
def test_factory_method_pattern(self):
five_dollars = MoneyFactory.dollar(5)
ten_dollars = five_dollars.times(2)
self.assertEqual(ten_dollars.amount, 10)
if __name__ == '__main__':
unittest.main()
道具(Imposter)模式
概念与应用场景
道具模式引入与现有对象协议相同但实现不同的对象,将新变化引入计算,避免大量条件逻辑。
示例代码
# 图形抽象类
class Figure:
def draw(self, brush):
pass
# 矩形图形类
class RectangleFigure(Figure):
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def draw(self, brush):
brush.log(f"rectangle {self.x} {self.y} {self.width} {self.height}\n")
# 椭圆形图形类
class OvalFigure(Figure):
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def draw(self, brush):
brush.log(f"oval {self.x} {self.y} {self.width} {self.height}\n")
# 绘制媒介类
class RecordingMedium:
def __init__(self):
self.log_content = ""
def log(self, message):
self.log_content += message
def get_log(self):
return self.log_content
# 测试代码
import unittest
class TestImposterPattern(unittest.TestCase):
def test_rectangle_drawing(self):
drawing = []
rectangle = RectangleFigure(0, 10, 50, 100)
brush = RecordingMedium()
rectangle.draw(brush)
self.assertEqual(brush.get_log(), "rectangle 0 10 50 100\n")
def test_oval_drawing(self):
drawing = []
oval = OvalFigure(0, 10, 50, 100)
brush = RecordingMedium()
oval.draw(brush)
self.assertEqual(brush.get_log(), "oval 0 10 50 100\n")
if __name__ == '__main__':
unittest.main()
组合(Composite)模式
概念与用途
组合模式实现对象行为由一组其他对象行为组合而成,将对象组合成树形结构,统一单个与组合对象使用方式。
示例代码
# 交易类
class Transaction:
def __init__(self, value):
self.value = value
def balance(self):
return self.value
# 账户类
class Account:
def __init__(self):
self.transactions = []
def add_transaction(self, transaction):
self.transactions.append(transaction)
def balance(self):
sum_value = 0
for transaction in self.transactions:
sum_value += transaction.balance()
return sum_value
# 总体账户类,用于组合多个账户
class OverallAccount:
def __init__(self):
self.accounts = []
def add_account(self, account):
self.accounts.append(account)
def balance(self):
sum_balance = 0
for account in self.accounts:
sum_balance += account.balance()
return sum_balance
# 测试代码
import unittest
class TestCompositePattern(unittest.TestCase):
def test_account_balance(self):
account = Account()
transaction1 = Transaction(100)
transaction2 = Transaction(200)
account.add_transaction(transaction1)
account.add_transaction(transaction2)
self.assertEqual(account.balance(), 300)
def test_overall_account_balance(self):
overall_account = OverallAccount()
account1 = Account()
account2 = Account()
transaction1 = Transaction(100)
transaction2 = Transaction(200)
account1.add_transaction(transaction1)
account2.add_transaction(transaction2)
overall_account.add_account(account1)
overall_account.add_account(account2)
self.assertEqual(overall_account.balance(), 300)
if __name__ == '__main__':
unittest.main()
收集参数(Collecting Parameter)模式
概念与实现方式
收集参数模式通过添加参数收集操作中分散于多个对象的结果,处理复杂期望结果时使代码结构清晰。
示例代码
# 表达式抽象类
class Expression:
def to_string(self, writer):
pass
# 加法表达式类
class Sum(Expression):
def __init__(self, augend, addend):
self.augend = augend
self.addend = addend
def to_string(self, writer):
writer.println("(")
writer.indent()
self.augend.to_string(writer)
writer.print(" + ")
self.addend.to_string(writer)
writer.dedent()
writer.println(")")