第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 = calculatorself.operand = operanddef execute(self):self.calculator.add(self.operand)# 减法命令
class SubtractCommand(Command):def __init__(self, calculator, operand):self.calculator = calculatorself.operand = operanddef execute(self):self.calculator.subtract(self.operand)# 计算器类
class Calculator:def __init__(self):self.result = 0def add(self, num):self.result += numdef subtract(self, num):self.result -= num# 测试代码
import unittestclass 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 = amountdef 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.amountdef __hash__(self):return hash(self.amount)# 测试代码
import unittestclass 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 = loggerdef do_something(self):self.logger.log("Doing something...")# 测试代码
import unittestclass 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.abstractmethoddef set_up(self):pass@abc.abstractmethoddef run_test(self):pass@abc.abstractmethoddef tear_down(self):pass# 具体测试用例类
class MyTestCase(TestCase):def __init__(self):self.result = 0def set_up(self):self.result = 0def run_test(self):self.result = 5 + 3def tear_down(self):self.result = 0# 测试代码
import unittestclass 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):passdef move(self):passdef 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 = modedef mouse_down(self):self.mode.select()def mouse_move(self):self.mode.move()def mouse_up(self):self.mode.unselect()# 测试代码
import unittestclass 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_messagedef 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 = reportdef print(self):self.report.print_report()# 测试代码
import unittestclass 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 = amountdef times(self, multiplier):pass# 美元类
class Dollar(Money):def times(self, multiplier):return Dollar(self.amount * multiplier)# 货币工厂类
class MoneyFactory:@staticmethoddef dollar(amount):return Dollar(amount)# 测试代码
import unittestclass 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 = xself.y = yself.width = widthself.height = heightdef 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 = xself.y = yself.width = widthself.height = heightdef 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 += messagedef get_log(self):return self.log_content# 测试代码
import unittestclass 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 = valuedef 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 = 0for 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 = 0for account in self.accounts:sum_balance += account.balance()return sum_balance# 测试代码
import unittestclass 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 = augendself.addend = addenddef 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 = amountself.currency = currencydef to_string(self, writer):writer.print(f"{self.amount} {self.currency}")# 缩进流类,用于格式化输出
class IndentingStream:def __init__(self):self.lines = []self.indent_level = 0def indent(self):self.indent_level += 1def dedent(self):self.indent_level -= 1def 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 unittestclass 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 = Nonedef __new__(cls):if cls._instance is None:cls._instance = super().__new__(cls)return cls._instance# 测试代码
import unittestclass 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.mzph.cn/web/67500.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【腾讯云】腾讯云docker搭建单机hadoop

这里写目录标题 下载jdk hadoop修改hadoop配置编写Dockerfile构建镜像运行镜像创建客户端 下载jdk hadoop wget --no-check-certificate https://repo.huaweicloud.com/java/jdk/8u151-b12/jdk-8u151-linux-x64.tar.gz wget --no-check-certificate https://repo.huaweicloud.…

设计模式 - 行为模式_Template Method Pattern模板方法模式在数据处理中的应用

文章目录 概述1. 核心思想2. 结构3. 示例代码4. 优点5. 缺点6. 适用场景7. 案例:模板方法模式在数据处理中的应用案例背景UML搭建抽象基类 - 数据处理的 “总指挥”子类定制 - 适配不同供应商供应商 A 的数据处理器供应商 B 的数据处理器 在业务代码中整合运用 8. 总…

HTML5+SVG+CSS3实现雪中点亮的圣诞树动画效果源码

源码介绍 这是一款基于HTML5SVGCSS3实现雪中点亮的圣诞树动画效果源码。画面中的圣诞树矗立在雪地中,天上飘落着雪花。当鼠标滑过圣诞树时,可见到圣诞树上的灯光闪烁,同时左下角探出雪怪模样的半个脑袋,四处张望着。整体画面栩栩…

C基础寒假练习(3)

一、求数组中的第二大值 #include <stdio.h> int main() {int arr[] {12, 35, 1, 10, 34, 1};int size sizeof(arr) / sizeof(arr[0]);if (size < 2) {printf("数组元素不足两个\n");return 0;}int first -2147483648, second -2147483648; // 使用IN…

C++,STL,【目录篇】

文章目录 一、简介二、内容提纲第一部分&#xff1a;STL 概述第二部分&#xff1a;STL 容器第三部分&#xff1a;STL 迭代器第四部分&#xff1a;STL 算法第五部分&#xff1a;STL 函数对象第六部分&#xff1a;STL 高级主题第七部分&#xff1a;STL 实战应用 三、写作风格四、…

【Node.js】Koa2 整合接口文档

部分学习来源&#xff1a;https://blog.csdn.net/qq_38734862/article/details/107715579 依赖 // koa2-swagger-ui UI视图组件 swagger-jsdoc 识别写的 /***/ 转 json npm install koa2-swagger-ui swagger-jsdoc --save配置 config\swaggerConfig.js const Router requir…

Maven的单元测试

1. 单元测试的基本概念 单元测试&#xff08;Unit Testing&#xff09; 是一种软件测试方法&#xff0c;专注于测试程序中的最小可测试单元——通常是单个类或方法。通过单元测试&#xff0c;可以确保每个模块按预期工作&#xff0c;从而提高代码的质量和可靠性。 2.安装和配…

论文阅读(八):结构方程模型用于研究数量遗传学中的因果表型网络

1.论文链接&#xff1a;Structural Equation Models for Studying Causal Phenotype Networks in Quantitative Genetics 摘要&#xff1a; 表型性状可能在它们之间发挥因果作用。例如&#xff0c;农业物种的高产可能会增加某些疾病的易感性&#xff0c;相反&#xff0c;疾病的…

LeetCode | 不同路径

一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff1f; 示例 1…

C++的类Class

文章目录 一、C的struct和C的类的区别二、关于OOP三、举例&#xff1a;一个商品类CGoods四、构造函数和析构函数1、定义一个顺序栈2、用构造和析构代替s.init(5);和s.release();3、在不同内存区域构造对象4、深拷贝和浅拷贝5、构造函数和深拷贝的简单应用6、构造函数的初始化列…

Excel 技巧21 - Excel中整理美化数据实例,Ctrl+T 超级表格(★★★)

本文讲Excel中如何整理美化数据的实例&#xff0c;以及CtrlT 超级表格的常用功能。 目录 1&#xff0c;Excel中整理美化数据 1-1&#xff0c;设置间隔行颜色 1-2&#xff0c;给总销量列设置数据条 1-3&#xff0c;根据总销量设置排序 1-4&#xff0c;加一个销售趋势列 2&…

Leetcode 131 分割回文串(纯DFS)

131. 分割回文串https://leetcode.cn/problems/palindrome-partitioning/https://leetcode.cn/problems/palindrome-partitioning/ 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 示例 1&#xff1a…

关于安卓greendao打包时报错问题修复

背景 项目在使用greendao的时候&#xff0c;debug安装没有问题&#xff0c;一到打包签名就报了。 环境 win10 jdk17 gradle8 项目依赖情况 博主的greendao是一个独立的module项目&#xff0c;项目目前只适配了java&#xff0c;不支持Kotlin。然后被外部集成。greendao版本…

一文讲解Java中的BIO、NIO、AIO之间的区别

BIO、NIO、AIO是Java中常见的三种IO模型 BIO&#xff1a;采用阻塞式I/O模型&#xff0c;线程在执行I/O操作时被阻塞&#xff0c;无法处理其他任务&#xff0c;适用于连接数比较少的场景&#xff1b;NIO&#xff1a;采用非阻塞 I/O 模型&#xff0c;线程在等待 I/O 时可执行其…

分布式系统架构怎么搭建?

分布式系统架构 互联网企业的业务飞速发展&#xff0c;促使系统架构不断变化。总体来说&#xff0c;系统架构大致经历了单体应用架构—垂直应用架构—分布式架构—SOA架构—微服务架构的演变&#xff0c;很多互联网企业的系统架构已经向服务化网格&#xff08;Service Mesh&am…

数据库备份、主从、集群等配置

数据库备份、主从、集群等配置 1 MySQL1.1 docker安装MySQL1.2 主从复制1.2.1 主节点配置1.2.2 从节点配置1.2.3 创建用于主从同步的用户1.2.4 开启主从同步1.2.4 主从同步验证 1.3 主从切换1.3.1 主节点设置只读&#xff08;在192.168.1.151上操作&#xff09;1.3.2 检查主从数…

代码随想录_栈与队列

栈与队列 232.用栈实现队列 232. 用栈实现队列 使用栈实现队列的下列操作&#xff1a; push(x) – 将一个元素放入队列的尾部。 pop() – 从队列首部移除元素。 peek() – 返回队列首部的元素。 empty() – 返回队列是否为空。 思路: 定义两个栈: 入队栈, 出队栈, 控制出入…

AJAX综合案例——图书管理

黑马程序员视频地址&#xff1a; AJAX-Day02-10.案例_图书管理AJAX-Day02-10.案例_图书管理_总结_V1.0是黑马程序员前端AJAX入门到实战全套教程&#xff0c;包含学前端框架必会的&#xff08;ajaxnode.jswebpackgit&#xff09;&#xff0c;一套全覆盖的第25集视频&#xff0c…

【编译原理实验二】——自动机实验:NFA转DFA并最小化

本篇适用于ZZU的编译原理课程实验二——自动机实验&#xff1a;NFA转DFA并最小化&#xff0c;包含了实验代码和实验报告的内容&#xff0c;读者可根据需要参考完成自己的程序设计。 如果是ZZU的学弟学妹看到这篇&#xff0c;那么恭喜你&#xff0c;你来对地方啦&#xff01; 如…

【redis进阶】分布式锁

目录 一、什么是分布式锁 二、分布式锁的基础实现 三、引入过期时间 四、引入校验 id 五、引入lua 六、引入 watch dog (看门狗) 七、引入 Redlock 算法 八、其他功能 redis学习&#x1f973; 一、什么是分布式锁 在一个分布式的系统中&#xff0c;也会涉及到多个节点访问同一…