創建可維護和可測試的 windows 窗體應用程式的 10 種方法(譯)

創建可維護和可測試的 windows 窗體應用程式的 10 種方法(譯)

我遇到的大多數 windows 窗體應用程式都不存在或單元測試覆蓋率極低。而且它們通常也很難維護,項目中各種 form 類的代碼背後有數百甚至數千行代碼,但它不必是這樣。

最后更新 2021/12/10 下午11:51
Mark Heath
预计阅读 8 分钟
分类
Blazor
标签
.NET Blazor Winform 單元測試

我遇到的大多數 windows 窗體應用程式都不存在或單元測試覆蓋率極低。而且它們通常也很難維護,項目中各種 form 類的代碼背後有數百甚至數千行代碼,但它不必是這樣。僅僅因為 windows 窗體是一項“遺留”技術,並不意味著你註定會造成無法維護的混亂。下面是創建可維護和可測試的 windows 窗體應用程式的十個技巧。

1. 用用戶控制項隔離你的用戶界面

首先,避免在一個表單上放置太多控制項。通常,你的應用程式的主要形式可以分解為邏輯區域(我們可以稱之為“視圖”)。如果將這些區域中的每個區域的控制項放入它們自己的容器中,那麼你自己的生活就會變得更加輕鬆,而在 windows 窗體中,最簡單的方法是使用用戶控制項。因此,如果你有一個資源管理器樣式的應用程式,左側是樹視圖,右側是詳細信息視圖,則將 treeview 放入其自己的 usercontrol,並為每個可能的右側視圖創建一個 usercontrol。同樣,如果你有選項卡控制項,請為選項卡控制項中的每個頁面創建一個單獨的 usercontrol。

這樣做不僅可以防止你的類變得難以管理,而且還可以調整大小和設置 tab 鍵順序等,使任務變得更加簡單。它還允許你在必要時輕鬆地一次性禁用用戶界面的整個部分。你還會發現,當你將用戶界面分解為包含邏輯分組控制項的較小 usercontrol 時,重新設計應用程式的 ui 布局會變得更加容易。

2. 將非 ui 代碼排除在後面的代碼之外

在 windows 窗體應用程式中,你總是會在窗體背後的代碼中找到訪問網絡、資料庫或文件系統的代碼。這嚴重違反了“單一責任原則”。你的 form 或 usercontrol 類的重點應該只是用戶界面。因此,當你檢測到背後的代碼中存在與 ui 無關的代碼時,請將其重構為具有單一職責的類。因此,你可以創建一個 preferencesmanager 類,或者一個負責調用特定 web 服務的類。然後可以將這些類作為依賴項注入到你的 ui 組件中(儘管這只是第一步--我們可以進一步擴展這個想法,我們很快就會看到)。

3. 用接口創建被動視圖

一種特別有用的技術是使你創建的每個窗體和用戶控制項都實現一個視圖接口。此接口應包含允許設置和檢索視圖中控制項的狀態和內容的屬性。它還可能包括報告用戶交互的事件,例如單擊按鈕或移動滑塊。目標是這些視圖接口的實現是完全被動的。理想情況下,你的 forms 和 usercontrols 背後的代碼中不應該有任何條件邏輯。

下面是一個用於新用戶條目視圖的視圖接口示例。這個視圖的實現應該是微不足道的。任何業務邏輯都不屬於後面的代碼(我們接下來將討論它屬於哪裡)。

interface INewUserView
{
    string FirstName { get; set; }
    string LastName { get; set; }
    event EventHandler SaveClicked;
}

通過確保你的視圖實現儘可能簡單,你將能夠最大程度地遷移到替代 ui 框架(如 wpf),因為你唯一需要做的就是在新技術中重新創建視圖。所有其他代碼都可以重複使用。

4.使用 presenters 控制視圖

因此,如果你已將所有視圖設為被動並實現接口,則你需要一些能夠實現應用程式業務邏輯並控制視圖的東西。我們可以稱這些為“presenter”類。這是稱為“模型視圖演示者”或 mvp 的模式。

在模型視圖展示器中,你的視圖是完全被動的,展示器會指示視圖顯示哪些數據。還允許視圖與演示者通信。在我上面的示例中,它通過引發事件來實現,但通常使用這種模式,你的視圖可以直接調用演示者。

绝对不允许视图开始直接操作模型(包括你的业务实体、数据库层等)。如果你遵循 MVP 模式,你的应用程序中的所有业务逻辑都可以轻松测试,因为它位于 Presenter 或其他非 UI 类中。

5. 為錯誤報告創建服務

通常,你的演示者類需要顯示錯誤消息。但不要只是將 messagebox.show 放入非 ui 類中。你將使該方法無法進行單元測試。而是創建一個服務(比如 ierrordisplayservice),你的演示者可以在需要報告問題時調用該服務。這使你的演示者單元保持可測試性,並且還提供了更改將來向用戶呈現錯誤的方式的靈活性。

6. 使用命令模式

如果你的應用程式包含一個帶有大量按鈕供用戶單擊的工具欄,則命令模式可能非常適合。命令模式規定你為每個命令創建一個類。這有很大的好處,可以將你的代碼分成小類,每個小類都有一個責任。它還允許你集中處理與特定命令有關的所有事情。是否應該啟用該命令?它應該是可見的嗎?它的工具提示和快捷鍵是什麼?它是否需要特定的特權或許可才能執行?命令運行時拋出的異常應該如何處理?

命令模式允許你標準化處理應用程式中所有命令所共有的每個問題的方式。你的命令對象將有一個 execute 方法,該方法實際上包含為該命令執行所需行為的代碼。在許多情況下,這將涉及調用其他對象和業務服務,因此你需要將它們作為依賴項注入到命令對象中。你的命令對象本身應該可以(並且直接)進行單元測試。

7. 使用 ioc 容器管理依賴項

如果你正在使用 presenter 類和 command 類,那麼你可能會發現它們所依賴的類的數量隨著時間的推移而增長。這是 unity 或 structuremap 等控制反轉容器真正可以幫助你的地方。無論它們具有多少級別的依賴關係,它們都允許你輕鬆構建視圖和演示器。

8. 使用事件聚合器模式

另一種在 windows 窗體應用程式中非常有用的設計模式是事件聚合器模式(有時也稱為“信使”或“事件總線”)。這是一種模式,其中事件的引發者和事件的處理者根本不需要相互耦合。當你的代碼中發生需要在其他地方處理的“事件”時,只需向事件聚合器發布一條消息即可。然後需要響應該消息的代碼可以訂閱和處理它,而無需擔心是誰提出的。

例如,你發送一條“請求幫助”消息,其中包含用戶當前在 ui 中的位置的詳細信息。然後另一個服務處理該消息並確保在 web 瀏覽器中啟動幫助文檔中的正確頁面。另一個例子是導航。如果你的應用程式有多個屏幕,則可以將“導航”消息發布到事件聚合器,然後訂閱者可以通過確保新屏幕顯示在用戶界面中來響應該消息。

除了從根本上分離事件的發布者和訂閱者之外,事件聚合器還具有創建極易進行單元測試的代碼的巨大好處。

9. 使用 async 和 await 進行線程處理

如果你的目標是 .net 4 及更高版本並使用 visual studio 12 或更高版本,請不要忘記你可以使用新的 async 和 await 關鍵字,這將大大簡化應用程式中的任何線程代碼,並自動處理回送後台任務完成後進入 ui 線程。它們還極大地簡化了跨多個鏈式後台任務的異常處理。它們非常適合 windows 窗體應用程式,如果你還沒有的話,非常值得一試。

10.不要太晚

可以将我上面描述的所有模式和技术改造为现有的 Windows 窗体应用程序,但我可以从痛苦的经验告诉你,这可能需要大量工作,尤其是当窗体背后的代码达到数千行时。如果你开始使用 MVP、事件聚合器和命令模式等模式构建应用程序,你会发现随着它们变得越来越大,维护起来会少很多痛苦。你还可以对所有业务逻辑进行单元测试,这对于持续的可维护性至关重要。

原文作者:mark heath

原文連結: https://markheath.net/post/maintainable-winforms

轉載自微信公眾號: onebyonedotnet

公眾號文章連結:https://mp.weixin.qq.com/s/ks_ghCRxMmOQPYFib0cb3g

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2024/2/29

winform中也可以這樣做數據展示

在做winform開發的過程中,經常需要做數據展示的功能,之前一直使用的是gridcontrol控制項,今天想通過一個示例,跟大家居間一下如何在winform blazor hybrid中使用ant design blazor中的table組件做數據展示。

继续阅读
同分类 / 同标签 2024/2/29

winform的界面也可以變好看?

前幾天跟大家居間了在winform中使用blazor hybrid,而且還說配上blazor的ui可以讓我們的winform程式設計的更加好看,接下來我想以一個在winform blazor hybrid中繪圖的例子來進行說明,希望對你有所幫助。

继续阅读
同分类 / 同标签 2024/1/7

碼坊“文章標題url別名生成器”上線

碼坊是站長新開的一個提供網頁在線工具、跨平台桌面和手機應用的開源項目。站長將終致力於為你帶來更高效、更便捷的使用體驗。今天,站長榮幸地推出“文章標題url別名生成器”,幫助你輕鬆創建文章標題的url別名,提升seo效果和用戶體驗。快來碼坊,探索更多實用工具吧!

继续阅读