当前位置: 首页 > article >正文

第30章 测试驱动开发中的设计模式解析(Python 版)

写在前面


这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 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(")")


# 货币类
class Money(Expression):
    def __init__(self, amount, currency):
        self.amount = amount
        self.currency = currency

    def to_string(self, writer):
        writer.print(f"{self.amount} {self.currency}")


# 缩进流类,用于格式化输出
class IndentingStream:
    def __init__(self):
        self.lines = []
        self.indent_level = 0

    def indent(self):
        self.indent_level += 1

    def dedent(self):
        self.indent_level -= 1

    def print(self, text):
        self.lines.append(" " * self.indent_level * 4 + text)

    def println(self, text=""):
        self.print(text)
        self.lines.append("")

    def contents(self):
        return "\n".join(self.lines)


# 测试代码
import unittest


class TestCollectingParameterPattern(unittest.TestCase):
    def test_expression_printing(self):
        sum_expr = Sum(Money(5, "USD"), Money(7, "CHF"))
        writer = IndentingStream()
        sum_expr.to_string(writer)
        self.assertEqual(writer.contents(), "( \n    5 USD + \n    7 CHF \n)")


if __name__ == '__main__':
    unittest.main()

单例模式(Singleton)

概念与应用

单例模式确保类只有一个实例,提供全局访问点,在无全局变量机制语言中实现类似功能。

示例代码

class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance


# 测试代码
import unittest


class TestSingletonPattern(unittest.TestCase):
    def test_singleton(self):
        instance1 = Singleton()
        instance2 = Singleton()
        self.assertEqual(instance1, instance2)


if __name__ == '__main__':
    unittest.main()

总结

上述介绍的设计模式在TDD中各有其独特价值。命令模式封装请求;值对象模式保证对象状态稳定;Null对象模式简化特殊情况处理;模板方法模式定义算法骨架;可插入对象和选择器模式提升灵活性;工厂方法模式优化对象创建;道具模式引入变化;组合模式处理对象组合;收集参数模式管理复杂结果;单例模式控制全局实例。

在实际TDD项目中,开发者应依据具体需求灵活选用设计模式,以优化代码结构,增强代码的可维护性、扩展性与复用性,开发出更优质的软件系统。


http://www.kler.cn/a/519926.html

相关文章:

  • 三年级数学知识边界总结思考-下册
  • GSI快速收录服务:让你的网站内容“上架”谷歌
  • 从Spring请求处理到分层架构与IOC:注解详解与演进实战
  • MYSQL数据库 - 启动与连接
  • 入门 Canvas:Web 绘图的强大工具
  • C#,入门教程(05)——Visual Studio 2022源程序(源代码)自动排版的功能动画图示
  • rust学习-rust中的格式化打印
  • 深度解读:近端策略优化算法(PPO)
  • 浅谈在AI时代GIS的发展方向和建议
  • Elasticsearch 性能测试工具 Loadgen 之 004——高级用法示例
  • c语言函数(详解)
  • Vue.js 高级组件开发
  • 任务一:Android逆向
  • 泷羽Sec-Powershell3
  • 设计模式思想的元规则
  • Python从0到100(八十五):神经网络与迁移学习在猫狗分类中的应用
  • 数据结构day02
  • go安全项目汇总表
  • 神经网络|(三)线性回归基础知识
  • Leetcode - 周赛433