如何在C#語言中實施架構規則

語言: CN / TW / HK

譯者 | 李睿

審校 | 梁策 孫淑娟

開發人員為了確保他們編寫的程式碼達到預期的目的,需要進行單元測試。有一些開源框架可用於對.NET應用程式進行單元測試,即NUnit和xUnit.Net。開發人員應該始終在軟體開發工作流程中加入單元測試,以減少或消除應用程式中的錯誤。

還可以利用ArchUnit或NetArchTest等框架來編寫有助於實施架構規則的單元測試。受基於Java的ArchUnit啟發,Ben Morris開發的NetArchTest是一個簡單的框架,可用於在.NET Framework或.NET Core以及.NET6專案中實施架構規則。

本文討論了在C#語言中執行架構規則的重要性以及如何利用NetArchTest來實現這一點。要使用本文中提供的程式碼示例,需要在系統中安裝Visual Studio 2022。

1.強制執行架構規則的必要性

有大量靜態程式碼分析框架和工具可用於檢查.NET、.NET Core或.NET 6中的程式碼質量。對於初學者來說,SonarQube和NDepend是兩個流行的工具。靜態程式碼分析也可作為Visual Studio的一部分。

然而,這些工具很少能幫助開發人員在原始碼中保留架構設計模式或強制執行架構規則。如果不定期驗證或執行這些規則,其應用程式的設計或架構將隨著時間的推移而退化。最終會發現維護程式碼庫已成為一項艱鉅的任務。

雖然靜態程式碼分析工具可以幫助開發人員驗證或強制執行通用最佳實踐,但可以利用NArchTest建立單元測試,以在其.NET、.NET Core和.NET 6應用程式中強制執行架構規則。這些包括程式碼庫中的類設計、命名和依賴關係的約定。

開發人員可以在單元測試方法中使用NArchTest,然後將這些測試方法合併到構建和釋出管道中,以便在每次簽入時自動驗證架構規則。

2.在Visual Studio2022中建立單元測試專案

首先,使用xUnit測試專案模板在Visual Studio 2022中建立單元測試專案。以下步驟將在Visual Studio 2022中建立一個新的單元測試專案:

(1)啟動Visual Studio 2022 IDE。 (2)點選“建立新專案”。 (3)在“建立新專案”視窗中,從顯示的模板列表中選擇“xUnit測試專案”。 (4)單擊下一步。 (5)在“配置新專案”視窗中,指定新專案的名稱和位置。 (6)根據開發人員的偏好,選擇“將解決方案和專案放在同一目錄”複選框。 (7)單擊下一步。 (8)在接下來顯示的“附加資訊”視窗中,從頂部的下拉列表中選擇.NET 6.0作為目標框架。將“身份驗證型別”保留為“無”(預設)。 (9)確保未選中“啟用Docker”、“為HTTPS配置”和“啟用開放API支援”複選框,因為不會在此處使用這些功能。 (10)單擊建立。

這將在Visual Studio 2022中建立一個新的xUnit專案。將在本文的後續部分中使用該專案。

3.在Visual Studio2022中建立類庫專案

現在在Visual Studio 2022中建立一個類庫專案。按照以下步驟將在Visual Studio 2022中建立一個新的類庫專案:

(1)啟動Visual Studio 2022 IDE。

(2)點選“建立新專案”。

(3)在“建立新專案”視窗中,從顯示的模板列表中選擇“類庫”。

(4)單擊下一步。

(5)在“配置新專案”視窗中,指定新專案的名稱和位置。

(6)單擊下一步。

(7)在接下來顯示的“附加資訊”視窗中,從頂部的下拉列表中選擇.NET 6.0作為目標框架。

(8)單擊建立。

這將在Visual Studio 2022中建立一個新的類庫專案。將在本文的後續部分中使用該專案。

4.在.NET 6中建立模型類

假設類庫專案的名稱是Core.Infrastructure。在解決方案資源管理器視窗中,選擇此專案,然後單擊“新增->新建資料夾”以將新的解決方案資料夾新增到專案中。模型應與其解決方案資料夾同名。

現在在Models解決方案資料夾中建立一個名為BaseModel的類並插入以下程式碼:

public abstract class BaseModel
    {
        public int Id { get; set; }
    }

再建立兩個名為Product和Customer的模型類。這兩個類中的每一個都應該擴充套件BaseModel類,如下所示:

public class Product: BaseModel
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class Customer: BaseModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

5.在.NET 6中建立服務類

在同一個專案中建立另一個解決方案資料夾,並將其命名為Services。在此解決方案資料夾中建立一個名為IBaseService的介面,併為其提供以下程式碼:

public interface IBaseService
{
    public void Initialize();
}

Initialize方法必須由實現此介面的所有類實現。Product Service和Customer Service類實現了IBaseService介面,如下面的程式碼片段所示:

//ProductService.cs
using Core.Infrastructure.Models;
namespace Core.Infrastructure.Services
{
    public sealed class ProductService: IBaseService
    {
        public void Initialize()
        {
            //Write your implementation here
        }
        public List<Product> GetProducts()
        {
            return new List<Product>();
        }
    }
}
//CustomerService.cs
using Core.Infrastructure.Models;
namespace Core.Infrastructure.Services
{
    public sealed class CustomerService: IBaseService
    {
        public void Initialize()
        {
            //Write your implementation here
        }
        public List<Customer> GetCustomers()
        {
            return new List<Customer>();
        }
    }
}

需要注意的是,出於這個簡單實現的目的,ProductService類和CustomerService類的Initialize方法都保留為空。開發人員可以為這些編寫自己的實現。

6.安裝NetArchTest.RulesNuGet包

現在將NetArchTest.RulesNuGet包新增到專案中。為此,需要在解決方案資源管理器視窗中選擇專案,然後右鍵單擊並選擇“管理NuGet包”。在NuGet包管理器視窗中,搜尋NetArchTest.Rules包並安裝它。

或者,可以通過NuGet包管理器控制檯輸入下面顯示的行來安裝包。

PM> Install-Package NetArchTest.Rules

7.在.NET6中編寫架構單元測試

最後,應該編寫架構單元測試來檢查被測原始碼是否符合標準。需要注意,此處的“標準”一詞是相對的,可以假設這些標準將由您定義。

以下測試方法驗證服務類的名稱是否帶有服務字尾。

public void ServiceClassesShouldHaveNameEndingWithService()
{
    var result = Types.InCurrentDomain()
                 .That().ResideInNamespace(("Core.Infrastructure.Services"))
                 .And().AreClasses()
                 .Should().HaveNameEndingWith("Service")
                 .GetResult();
    Assert.True(result.IsSuccessful);
}

可以使用另一條規則來驗證所有服務類是否都實現了IBaseService介面。以下測試方法說明了如何實現這一點。

public void ServiceClassesShouldImplementIBaseServiceInterface()
{
   var result = Types.InCurrentDomain()
                .That().ResideInNamespace(("Core.Infrastructure.Services"))
                .And().AreClasses()
                .Should().ImplementInterface(typeof(IBaseService))
                .GetResult();
   Assert.True(result.IsSuccessful);
}

還可以有一個規則來驗證服務類是公共的而不是密封的。如果這些類是密封的,將無法進一步擴充套件它們。

public void ServiceClassesShouldBePublicAndNotSealed ()
{
    var result = Types.InCurrentDomain()
                .That().ResideInNamespace(("Core.Infrastructure.Services"))
                .Should().BePublic().And().NotBeSealed()
                .GetResult();
    Assert.True(result.IsSuccessful);
}

當執行這些測試方法時,應該會發現它們都通過了測試,也就是取得了成功。嘗試更改程式碼並重新執行測試,以檢查是否符合討論的規則。

正在執行NetArchTest單元測試

需要記住的是,在較新版本的C#語言中,可以在介面中擁有成員的預設實現。因此,如果有一個介面由一個或多個類實現,可以在介面中編寫預設實現。如果編寫的程式碼在介面的所有實現中都是通用的,就是正確的。

原文連結:https://www.infoworld.com/article/3656703/how-to-enforce-architecture-rules-in-csharp.html