“程序源代碼中任何可能表明存在更深層問題的癥狀。”
在Java中, 靜態方法允許您在“類范圍”內執行代碼,而不是像成員方法這樣的實例范圍。 這意味著,它們依賴于類級別的變量(如果有),傳遞給靜態方法的參數或任何其他全局可訪問的數據。 它們不是面向對象的。 對象具有與之關聯的狀態,并且只能通過實現該對象“行為”的方法進行操作。 靜態方法不在狀態上操作,它們不是面向對象的,實際上它們是過程式的。
這不好嗎?
不會。盡管Java是面向對象的,但有時還是需要和/或首選Java中的類似于過程的編程。 任何面向對象的語言的真正威力在于能夠在代碼中緊密實現現實生活中的系統模型的能力(請參閱我有關面向對象建模的文章 )。 但是,即使在最核心的對象模型中,也很可能會有一些粘合代碼或將以過程樣式實現的基礎結構代碼。
因此,如果Java中的類似于過程的編程不是“那么糟糕”并且靜態方法是過程編程的一種形式,那么靜態方法是否不好?
嗯……答案并不像是“是”或“否”那么簡單,無論您在其他博客上會讀到什么,但我可能會不斷爭論著為什么這實際上是必須在上下文中做出的決定,因此,讓我們重點關注一下我在Michael Minella博客的“如何模擬靜態方法”中遇到的一組語句:
“已經成為該語言基礎知識的部分(您要做的只是看一下Apache Commons項目以了解這一點)非常糟糕,以測試為名必須不惜一切代價避免。 Gosling(或其團隊中的某人)出于某種原因將其放入語言中,并且僅由于您的工具集不支持對它的測試是無稽之談而避免使用這些語言。 是時候獲得新的工具集了。”
首先,我想指出的是,僅僅因為某種東西已經成為一種語言的基本組成部分,并不意味著它就是“好”或應該做的事情。 查看已檢查的異常以供參考。 我記得EJB 1.x和2.x在過去成為Java EE的“基礎”部分,因此也請參考一下。
其次,盡管我在理論上確實同意Michael的觀點,即由于您的工具不支持某種特定的語言功能而使其愚蠢,但他的前提是靜態方法。 避免使用靜態方法是因為您的工具不支持靜態方法,這根本不是胡說。 實際上,由一些好的測試和/或模擬框架( Mockito是我最喜歡的框架)引起的阻抗類型 )和靜態方法可以確定地識別為代碼異味。 這并不意味著我們不應該這樣做,而是應該付出更多的努力來理解我們為什么這樣做,并在存在“更深層次的問題”時探索替代方法。
我想指出,至少有兩種類型的靜態方法通常不會在測試/模擬框架中表現出太大的阻力。 第一種類型是用作實用程序方法的靜態方法,就像在許多apache commons庫或您自己的內部commons庫中找到的方法一樣。 這些通常是支持特定方法目標的例程,并且將它們模擬/存根到單元測試之外是沒有意義的。 它們是實現的一部分,因此應進行測試。 第二種類型是靜態方法,用于代替構造函數,如Joshua Bloch在他的書《 Effective Java》中所展示的。 靜態方法的這種使用使您可以使用名稱具有非常描述性的方法來構造新對象,以及其他一些優點。 第二種靜態方法的分支可能包括工廠方法,但這取決于上下文。
當單元依靠靜態方法執行超出該單元職責范圍的邏輯時,由于靜態方法和測試框架阻抗而產生的最明顯的代碼異味。 在這些情況下,您的測試框架將對您不利,因為您無法對范圍外的邏輯進行存根/模擬,因為它是通過靜態方法“硬編碼”的。 這可以被視為“更深層的問題”,并且是大多數博客的焦點,這些博客告訴您不要使用靜態方法,因為測試變得異常困難或不可能。 更改設計方法以遵循依賴性反轉原則是另一種選擇。 對如何測試單元的更好的理解是另一個。
我強烈斷言,在使用靜態方法的情況下,您可能會從測試框架中得到的回退表示代碼有氣味,而不是您需要嘗試找到一個使用復雜的欺騙手段并將類加載器重新映射作為解決方案的框架。 應該準備評估一種特殊方法在其設計中的用途和基本缺陷。 Michael的博客文章使讀者太容易采用新的工具/框架,僅因為Java支持靜態方法并且您當前的測試框架闡明了一個阻抗-在這種情況下,阻抗反映了代碼的味道,需要一些更深入,更批判性的思考。
參考: Java靜態方法可能是 JCG合作伙伴 Christian Posta在Christian Posta Software博客上的代碼味道 。
翻譯自: https://www.javacodegeeks.com/2012/05/java-static-methods-can-be-code-smell.html