第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.pswp.cn/web/67500.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67500.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67500.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2025美賽復盤總結反思(論文手)

充實的經歷&收獲 美賽這個過程,確實逼著自己學了不少東西,excel本身,以及發現Ai確實能幫忙處理不少的了,也第一次發現原來自己熬通宵也能很精神(當然確實是傷身體的) 好的經驗: 積極搜索…

從0開始使用面對對象C語言搭建一個基于OLED的圖形顯示框架(協議層封裝)

目錄 協議層設計,以IIC為例子 關于軟硬件IIC 設計的一些原則 完成協議層的抽象 刨析我們的原理 如何完成我們的抽象 插入幾個C語言小技巧 完成軟件IIC通信 開始我們的IIC通信 結束我們的IIC通信 發送一個字節 (重要)完成命令傳遞和…

舉例說明python單利模式的必要性

單例模式的核心目的是確保一個類只有一個實例,并提供一個全局訪問點來獲取這個實例。這種設計模式在某些場景下非常必要,尤其是在需要嚴格控制資源訪問、共享狀態或配置管理的場景中。下面通過幾個具體的例子來說明Python中單例模式的必要性。 1. 數據庫…

【騰訊云】騰訊云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…

【linux三劍客】grep練習題

題目 進入/lianxi目錄&#xff0c;復制/etc/passwd到當前目錄下&#xff0c;然后對passwd進行操作查找出當前passwd文件中以ftp或者mail開頭的行&#xff0c;在屏幕上輸出。查找出當前passwd文件中有沒有以r、m、f開頭的行&#xff0c;在屏幕上輸出。查找出當前passwd文件中以…

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…

電梯系統的UML文檔14

對于 HallButtonControl&#xff0c;我們有二個狀態: "門廳燈開 " 和 " 門廳燈關"。 從給出的初始信息&#xff0c;初始的狀態應該是"門廳燈關"。行為定義&#xff1a; " 當 HallCall[f&#xff0c;d]是真&#xff0c;則指令 HallLight[f&…

關于安卓greendao打包時報錯問題修復

背景 項目在使用greendao的時候&#xff0c;debug安裝沒有問題&#xff0c;一到打包簽名就報了。 環境 win10 jdk17 gradle8 項目依賴情況 博主的greendao是一個獨立的module項目&#xff0c;項目目前只適配了java&#xff0c;不支持Kotlin。然后被外部集成。greendao版本…

SQL server 數據庫使用整理

標題&#xff1a;SQL server 數據庫使用整理 1.字符串表名多次查詢 2.讀取SQL中Json字段中的值&#xff1a;JSON_VALUE&#xff08;最新版本支持&#xff0c;屬性名大小寫敏感&#xff09; 1.字符串表名多次查詢 SELECT ROW_NUMBER() OVER (ORDER BY value ASC) rowid,value…

一文講解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 時可執行其…