c# - 編碼規範 - 是一個初始化方法的代碼味道?




visual studio project naming (11)

你的構造函數可以啟動後台線程來執行加載。 然後,需要使用該數據的類中的任何內容都將檢查異步加載是否已完成,如果不等待。

這使得類的建設速度很快,它隱藏了你的類消費者的所有多線程細節,並且擺脫了醜陋的init模式。

我正在編寫一大堆系統。 他們不是從一個共同的接口派生的。

一些示例係統: MusicSystemPhysicsSystemInputSystem等等。

目前, MusicSystem在其構造函數中加載了大量的音頻文件,因此,當對象第一次創建時,可能會有一些短暫的滯後。

因此,這個代碼加載所有的音頻文件應該放在一個Initialize()方法嗎? 這允許程序員確定何時加載音頻文件,但是如果忘記調用Initialize() ,程序將會崩潰。

因為並不是所有的系統都需要一個Initialize()方法,所以程序員必須查看每個系統,看看這個類是否有Initialize()方法,如果是的話,調用它。 這有點麻煩。

就一般設計原則而言,哪種方法更可取?


你需要考慮你的*系統的碎片,因為它看起來像你有一個超級主控型,它統治著整個世界,並且在構造函數中完成大部分的工作。 哪個不太好,看起來很臭,破壞了固體原理。

您可以將長時間運行的IO操作移出到特定的包裝器中,​​然後您可以在.Net中傳遞參數,例如Stream,Connection,IDataReader等。 如果操作可能消耗大量的CPU,內存或IO吞吐量,那麼這將是更不可預測的。


基本上,你正試圖平衡程序效率和程序員效率。 所以在這種情況下不一定是代碼味道,這取決於哪個更重要。 你寧願讓代碼稍微容易使用,難以破解,或讓程序加載速度更快?

但是,您可以嘗試的另一種方法是延遲加載

那麼,你也可以吃你的蛋糕,

  • 客戶端代碼將不必調用Initialize
  • 除非需要,否則數據將不會被加載

如何執行初始化的私有/受保護的方法,並在需要初始化的任何方法執行時調用內部初始化?

例如

public class MyClass
{
  private bool _isInitialized;

  public MyClass()
  {
    ... only basic initializations...
  }

  private void initialize()
  {
    if (_isInitialized)
      return;

    // initialize here
  }

  public void SimpleMethod()
  {
    // doesn't need to initialize
  }

  public void ComplexMethod()
  {
    initialize();

    // do something...
  }
}

如果音頻文件總是相同的。 你可以嘗試從一個靜態屬性加載它們。 一旦對象加載音頻文件。 如果音頻文件列表存儲在一個靜態屬性中,它們將對所有人都可用。


將大量初始化移出構造函數不是一種代碼異味。

但是,依靠外部調用者來調用這個初始化是一種氣味 - 這就是所謂的時間耦合

在你的類上創建Initialize()方法,但將它設置為private (或者必須protected )。

另外編寫一個EnsureInitialized()方法,如果需要可以觸發初始化,但每個實例只能執行一次。

然後,您的類的每個公共入口點都應該在開始時調用EnsureInitialized() - 初始化會延遲到第一次使用的位置。

在這個位置上,如果你對鎖定感到滿意,你可以把一個調用EnsureInitalized()放到後台線程中,在後台執行這個工作,而對前台的影響最小。


我認為一個好主意是證明特定的類對於實例化是“昂貴的”。 程序員只有在絕對需要使用的時候才應該實例化對象。


為什麼不能有一個重載的構造函數,用一個布爾值來允許程序員指定他們是否想要通過昂貴的初始化方法。

然後,你不必檢查一個特殊方法的存在。


起初我會說這不是代碼異味,因為它允許程序員決定何時初始化一個對象,比如說,通過使用工作線程並行執行所有對象。

但是後來我想,這是一種代碼異味,因為同步或異步構造的選擇可以很容易地在構造函數本身中實現,也許構造函數參數可以相互偏好。

然後我想,怎麼樣的情況下,不可能拋出異常? 通過設置構造函數中的所有數據,如果沒有像IsInitialised或其他類型的異常,則無法指示失敗。 (Symbian是一個不支持例外的操作系統的例子。)

所以我想這個臭味真的取決於你工作的環境和你自己的個人喜好。


這根本不是“代碼味道”。 有這樣的事情並不罕見。 例如,查看.NET中的SqlConnection類。 有一個默認的構造函數不接受任何參數,並且有一個接受連接字符串的構造函數。 如果要連接到數據庫並使用連接超時的默認值等,那麼接受連接字符串的方法是非常方便的。但是,如果要更改這些屬性,或者如果要準備好連接實例,而要控制更多恰好當它連接時,你調用默認的構造函數,設置屬性,然後在準備好連接時調用Open


當然不是。 根據MSDN的指導方針,一個構造函數應該做最少的工作。 因此,像資源分配或重對像初始化這樣的事情需要放在其他地方像Initialize()。

https://msdn.microsoft.com/en-us/library/ms229060(v=vs.110).aspx

除了捕獲構造函數參數外,構造函數不應該做很多工作。 任何其他處理的成本應該推遲到需要的時候。





coding-style