簡體   English   中英

循環類引用不良做法?

[英]Circular class references bad practice?

最近有人告訴我,以Class 1調用Class 2調用Class 1形式的循環引用被認為是極端不好的做法,而我只是無法理解。 我的意思是,如果這些類位於兩個不同的項目中,那么我會完全理解問題所在,但是如果它們屬於同一項目,那會很糟糕嗎? 在某些情況下...您到底如何防止這種情況發生?

例如:我有某種服務器。 客戶端連接到該客戶端,而從套接字派生或持有該客戶端的客戶端則負責處理網絡內容以及一些信息,例如帳戶ID等。當有新內容時,此客戶端將調用數據包處理程序,現在數據包處理程序需要來自客戶端的信息,並且必須將信息發送回去。 我將客戶端傳遞給數據包處理程序,以便它可以調用它的發送函數,等等。

關心這個問題的人以為這是提到的不良做法,盡管我很少見到服務器,尤其是大型服務器,但這些服務器將所有數據包處理都保留在客戶端類中,但他根本沒有這樣做。 此外,您可能會比處理程序走得更遠,並調用更多的類。 將所有內容保留在客戶端中一團糟。 所以... 真的是不好的做法嗎?

如果是這樣,人們將如何解決呢? 為了做到這一點,您需要在Client中添加或多或少復雜的對象集合,您可以將其傳遞下來,而不必再次調用Client函數...

就像我說的那樣,我無法真正解決這個“問題”。 有人可以幫我嗎?

人們談論循環引用時,通常會談論項目/ dll引用。 這些在解析引用時是有問題的,Visual Studio不允許您添加循環引用。

但是您指的不是項目結構,而是體系結構,這里的事情要復雜一些。 相互調用的類沒有內在的錯。 實際上,這在.NET中的回調和事件等功能的設計中是隱含的-當您注冊事件時,實際上是在調用一個類,該類隨后將使用事件處理程序進行回調。

但是,這種形式的循環調用是相對分離的。 服務器沒有對客戶端的EXPLICIT引用,而只有被調用的訂閱客戶端列表。 如果您還沒有這樣做,而是讓數據包處理程序擁有例如對客戶端的顯式引用,則這兩個類將緊密耦合-數據包處理程序依賴於客戶端的特定實現,反之亦然。

為什么這樣不好? 我認為這違反了關注點分離原則,這是編程中最基本的概念之一。 客戶端應該知道如何處理客戶端操作,數據包處理程序應該處理數據包操作,也不應該知道另一方如何工作,並且只能通過定義明確的特定接口進行通信。

讓我們以基於您的OP的非常簡單的假設情況為例,該情況具有循環引用:客戶端調用數據包處理程序的Send()方法。 數據包處理程序現在開始連接,然后發現它需要用戶名/密碼。 它在客戶端上調用方法來獲取它,然后將其發送到服務器,獲取響應,然后再回調給客戶端以將其返回。

在這種情況下,數據包處理程序現在綁定到客戶端的實現。 它要求客戶端具有GetCredentials()方法和MessageReceived方法以對其進行回調。 現在想象一個更分離的場景:

客戶端首先注冊處理程序的ResponseReceived事件。 現在,客戶端調用數據包處理程序的Send()方法。 數據包處理程序需要身份驗證,因此會失敗-它將引發異常或返回錯誤代碼“無法連接”。 客戶端獲得此響應,然后再次使用Send(username,password)方法再次調用。 它成功,獲得響應,並引發ResponseReceived事件,將響應發送給訂閱該事件的任何人。

這允許數據包處理程序在其他客戶端的其他上下文中重用。 它允許在客戶端或處理程序內部進行更改,而對其他組件的影響較小。 它使代碼更簡單,更容易維護。 那很好。 :)

當然,在某些情況下,循環類引用一點也不壞。 總是存在實例化問題(雞肉或雞蛋)。 永遠記住,對象之間已經存在兩種通信方式。 您可以通過調用一個在有新消息時返回的函數來使客戶端等待下一個消息。

但這是您應該停下來思考的地方。 特別是在以下情況下(還有更多,但這些都在我的頭上):

  • 當您通知一個班級時,發生了一些動作->使它成為一個事件。
  • 當是一對一關系時->應該將類型合並為一個。
  • 當您的類擴展了另一個類的功能時->從其繼承而不是傳遞引用。

在您的情況下,客戶端<->數據包處理程序。 我可能不會在此處保留循環引用。 我可能會為這兩個類編寫一個控制器,以充當它們之間信息的代理。 我也不會將帳戶ID存儲在客戶端對象中。 這與我喜歡堅持的單一責任原則有些沖突。 我可能會得到這樣的結果:

  • 客戶端->套接字(負責網絡事務)
  • ClientHandler(負責控制流程)
    • 客戶
    • 包處理程序
    • 附加信息
  • 包處理程序

同一包裝內的類應被視為具有高度凝聚力(假設您的包裝結構正確!)並且可以具有更緊密的耦合。 在包裝中,循環關系(如果需要)是可以的,但是將從基於特定接口的設計中受益(如先前的張貼者所述)。

跨包裝邊界 ,內聚力自然較低,耦合應保持盡可能低:絕對沒有循環關系。

您可以嘗試提取A調用B的功能以及B調用A的功能,然后將它們封裝在類庫C中,其他庫可以毫無問題地調用它們。 顯然,C不能同時引用A和B。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM