dotNET Core:編碼規範

在項目開發過程中,由於時間緊、任務重,很容易導致面向功能編程。實現相同的功能,代碼可以寫的很優雅,也可以寫的很晦澀和複雜。現在的工作,都需要進行團隊協作,代碼就需要有一定的規範進行指引,因爲我們需要寫出讓人可以輕易讀懂的代碼,而不僅僅是機器。

規範沒有絕對的標準,遵循大部分人都認可的一種方式就可以了,保持統一。比如在 dotNET Core 中,我們可以參考下 dotNET Core 的源碼,最終制定一個適合團隊的規範即可。

下面是我理解的正確的一些規範:

基本準則

1、命名的規範分爲兩種:Pascal(大駝峯)和駝峯(小駝峯),示例如下:

• Pascal:UserName
• 駝峯:userName

2、命名要有意義,需要看到名稱知其含義。少用拼音和匈牙利命名法。

示例
Int price=20;
UserInfo userInfo=GetUserInfo(userId);
Int p=20; ×
Int intPrice=20; ×

3、對於類的成員變量,用this關鍵字,增強代碼可讀性。
4、對於基類的成員變量,用base關鍵字,增強代碼可讀性。

名稱規範

好的名稱可以讓我們減少很多不必要的註釋,可以讓代碼閱讀者很容易就理解代碼的意思。但命名不是一件容易的事情,在命名的時候,通常伴隨着我們對代碼邏輯的思考。比如:如果你不能給一個函數很準確地命名,那可能這個函數的職責不單一,做的事情太多,才導致一個名稱很難概括,意味着代碼可能需要重構。好的命名是我們寫好代碼的基礎。

命名空間

命名空間採用Pascal命名法:

namespace Fw.Application{}
namespace Fw.SmartFlow.Acitivity{}

實際工作中,我們會將很多邏輯上屬於同一類的文件,在物理上分成不同的目錄,這時建議修改命名空間爲相同的命名空間。

類採用Pascal命名法:

public class UserService{}

類是對屬性和方法的封裝,類有很多的種類:

  • 跟數據庫表對應的實體類
  • 處理業務邏輯的業務類
  • 提供擴展方法的擴展類
  • 接口層的數據傳輸類

不同的種類可以約定俗成地進行一些名稱的約束,比如擴展類用 Extension 結尾、接口層的使用 Request、Response 結尾,等等,這樣在閱讀代碼是就知道什麼類型的職責是什麼。

接口

接口採用大寫I+Pascal命名法:

public interface IUserService{}

方法

方法採用Passcal命名法:

public string GetUserName(){}

方法的命名需要比較具體,越抽象的名稱越難以理解。例如,InitData() 就是一個不太好的名稱,改成 InitConfiginfo() 會更好些。另外,方法的命名在同一類型的語義下要保持一致,在一些項目中看到查找類的方法,有 GetXXX、QueryXXX、FindXXX 等等。五花八門的方式會提升閱讀的成本。

變量

變量分爲:類變量、靜態類變量、只讀變量、靜態只讀變量、方法變量。

類變量:

private string _userName;

類變量只能使用 private 修飾符,如果需要暴漏出去,或是給子類使用,使用屬性來進行封裝。

靜態類變量、只讀變量、靜態只讀變量:

private static readonly ResourceManager _resourceManager;
public static readonly IRouter Instance = new MockRouter();

• 修飾符爲 private: _ + 駝峯命名法;
• 修飾符爲 public: Pascal 命名法;
• 修飾符爲 protected:Pascal 命名法;

方法變量:

bool isCheck;

常量:

常量採用Pascal命名法:

public const string AuthorizationFilter = "Authorization Filter";

屬性:

屬性採用 Pascal 命名法:

public BasicApiContext DbContext { get; }

參數

參數採用駝峯命名法:

public List<BizApp> GetBizAppList(string userId,DeviceType deviceType)

註釋規範

註釋是一把雙刃劍,當代碼中存在大量的註釋的時候,我們第一反應會先看註釋,並會默認註釋中寫的內容是對的,真實情況是註釋往往會給我們錯誤的指導。有兩個原因:

  • 當代碼邏輯放生變化時,只修改了代碼,註釋沒有調整;
  • 不同的人員都對註釋進行編輯,慢慢註釋會變得和代碼不匹配。

Martin Fowler 在他的經典書籍 《重構》 中也提到過多的註釋是一種壞味道的體現,他認爲,當你覺得需要寫註釋的時候,應該先想想是不是可以進行重構。那是不是程序中就不應該出現註釋呢?當然不是,我認爲下幾種情況是需要寫註釋的:

  • 時間緊急,臨時寫的一些 ”爛代碼“,需要寫註釋,說明原因,一般需要加上 TODO ;
  • 複雜算法類的方法,可以寫註釋說明邏輯;
  • 寫的是偏底層的類庫,對外暴露的方法需要寫註釋,調用方能方便在智能提示中顯示;
  • 如果你的能力寫不好代碼,那還是先把註釋寫上吧。

異常規範

  • 異常的目的是用來報告錯誤,這也是他的唯一目的,所以避免在返回值中來返回錯誤信息,所有的地方都應該使用拋異常的方式來報告錯誤;
  • 使用拋異常的方式可以防止錯誤的操作繼續執行;
  • 要能夠預估到會出現什麼異常,知道是什麼類型的異常,才 Try 住做相關的處理;
  • 最終用戶友好和對開發者友好;
  • 暴漏問題比隱藏問題要好,隱藏問題只會導致更嚴重的問題。

詳細的異常處理可以參考之前的文章:

  • dotNET:怎樣處理程序中的異常(理論篇)?
  • dotNET:怎樣處理程序中的異常(實戰篇)?

空行規範

空行規範是一個很簡單的規範,就是在每個方法中,代碼應該按照不同邏輯的邏輯塊進行分割顯示,雖然簡單,但如果不注意,還是會對代碼的閱讀帶來很大的障礙。下面看看 dotNET Core 的源碼 CreateDefaultBuilder 方法:

public static IHostBuilder CreateDefaultBuilder(string[] args)
{
    var builder = new HostBuilder();

    builder.UseContentRoot(Directory.GetCurrentDirectory());
    builder.ConfigureHostConfiguration(config =>
    {
        config.AddEnvironmentVariables(prefix: "DOTNET_");
        if (args != null)
        {
            config.AddCommandLine(args);
        }
    });

    builder.ConfigureAppConfiguration((hostingContext, config) =>
    {
        var env = hostingContext.HostingEnvironment;

        config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
              .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

        if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
        {
            var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
            if (appAssembly != null)
            {
                config.AddUserSecrets(appAssembly, optional: true);
            }
        }

        config.AddEnvironmentVariables();

        if (args != null)
        {
            config.AddCommandLine(args);
        }
    })
    .UseDefaultServiceProvider((context, options) =>
    {
        var isDevelopment = context.HostingEnvironment.IsDevelopment();
        options.ValidateScopes = isDevelopment;
        options.ValidateOnBuild = isDevelopment;
    });

    return builder;
}

想想看,上面代碼中如果去掉空行會讀起來是什麼樣的感受?

日誌規範

在一個完整的系統中,日誌非常的重要。在 dotNET Core 中自帶了日誌功能,當然我們也可以使用第三方的 NLog、Serillog 等。

這些日誌框架都提供日誌級別功能,比如:INFO、DEBUG、WARN 和 ERROR 等,這些級別對程序出錯時的排查非常有用,所以在記錄日誌時一定不要都使用 INFO 或者都使用 ERROR 了。

除了級別,日誌的類型有這麼幾類:

  • 操作日誌
  • 業務日誌
  • 錯誤日誌

操作日誌

系統中所有的操作的都記錄下來,包括登錄、數據的增刪改等,主要用來做審計,數據異常操作時的追責等。

隨着時間的推移,日誌的數據量會越來越大,所以需要考慮存儲的方式,比如階段性地將歷史日誌進行存檔。

業務日誌

用戶在界面中輸入數據,點擊一個按鈕,程序中會進行一系列的處理最終返回結果給用戶,在這個過程中對一些關鍵的業務信息進行記錄,可以在系統出現問題時方便排查和追蹤。

錯誤日誌

錯誤日誌的主要目的就是排錯,所以記錄的信息一定要是對排錯有幫助的信息,儘可能地記錄詳細,比如整個堆棧信息、調用鏈等。比如,你去進行錯誤排查時,發現記錄的信息是”未將對象引用到對象的實例“,你依然不知道錯誤的原因是什麼。

總結

談及代碼的時候,都會去聊架構、模式,這些固然重要,但編碼習慣和規範也不可小視。一份產品的代碼怎樣才能變得越來越好,這需要團隊每個人成員共同的努力,一個人掉鏈子,很容易就形成破窗效應,導致壞味道越來越多。

寫好的代碼,是每個有追求的技術人的使命和職責。

希望本文對您有所幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章