CodeGym /Java Blog /Toto sisi /Java 中的模式和單例
John Squirrels
等級 41
San Francisco

Java 中的模式和單例

在 Toto sisi 群組發布
本文的目標讀者是那些第一次遇到設計模式概念、聽說過單例這個術語,或者以某種方式實現了單例模式但不明白髮生了什麼的人。歡迎!CodeGym 學生第一次遇到設計模式是在第 15 級,當時隊長出人意料地要求他們通過使用惰性實現實現 Java 單例模式來“強化”他們的理解。第一次聽說單例模式的同學們瞬間會有很多疑問:設計模式到底是什麼?我們為什麼需要它?什麼是單例?最後,什麼是惰性實現?讓我們按順序回答這些問題。

世界上什麼是設計模式?

我相信一點歷史是為了以最好的理解來回答這個問題。有四位著名的編程作者(Erich Gamma、John Vlissides、Ralph Johnson 和 Richard Helm)提出了一個有趣的想法。他們注意到軟件開發通常要求他們解決大致相同的問題並編寫結構相同的代碼。因此他們決定描述在面向對象編程中經常需要使用的典型模式。他們的書於 1994 年以“設計模式:可重用面向對象軟件的元素”為標題出版。這本書的名字太長了,人們開始簡單地稱之為四人幫的書。第一版包括 23 種模式。之後,發現了數十種其他模式。
設計模式是針對常見問題的標準化解決方案。
單例模式只是其中之一。

為什麼我們需要設計模式?

您可以在不知道模式的情況下進行編程:畢竟,到了第 15 級,您已經在 CodeGym 上編寫了數百個小程序,甚至不知道它們的存在。這表明設計模式是一種工具,其用法將大師與業餘愛好者區分開來:設計模式描述瞭如何正確解決典型問題。這意味著了解模式可以節省您的時間。這樣,它們類似於算法。例如,您可以 使用二十一點和數字創建自己的排序算法並花很多時間這樣做,或者你可以實施一個已經被理解和描述了很長時間的方法。設計模式也是如此。此外,使用設計模式,代碼變得更加標準,並且當使用適當的模式時,您犯錯誤的可能性就會降低,因為很久以前就已經識別並消除了該模式的常見缺陷。最重要的是,模式知識有助於程序員更好地相互理解。您可以簡單地說出模式的名稱,而不是試圖向您的程序員同事提供冗長的解釋。總而言之,設計模式可以幫助您:
  • 不重新發明輪子,而是使用標準解決方案;
  • 標準化代碼;
  • 標準化術語;
總結本節,我們注意到整個設計模式可以分為三大類: 模式和單例 - 對於第一次接觸它們的每個人 - 2

最後是單例模式

單例模式是一種創建模式。這種模式確保一個類只有一個實例,並為該對象提供一個全局訪問點。從描述中可以清楚地看出,這種模式應該應用於兩種情況:
  1. 當您的程序要求只創建一個特定類的對象時。例如,一個電腦遊戲可能有一個 Hero 類,並且只有一個 Hero 對象來描述遊戲中唯一的英雄。

  2. 當您需要提供一個對象的全局訪問點時。換句話說,您需要使該對像在程序的任何地方都可用。las,僅僅創建一個全局變量是不夠的,因為它沒有寫保護:任何人都可以更改變量的值,因此對象的全局訪問點可能會丟失。Singleton的這些屬性是必需的,例如,當您有一個與數據庫一起工作的對象時,您需要從程序的不同部分訪問數據庫。單將確保沒有人編寫代碼來替換先前創建的實例。
所以Singleton滿足了這兩個需求:程序中必須只有某一類對象,並且必須可以全局訪問它。在第 15 關的示例中,船長要求您為以下任務實現此模式:
  1. 查找具有惰性初始化的單例示例。

  2. 使用相同的原則在不同的文件中創建三個單例類——Sun、Moon、Earth。

  3. 實施行星SunMoonEarth類中的接口。

  4. 在Solution類的靜態塊中調用readKeyFromConsoleAndInitPlanet方法。

  5. 實施readKeyFromConsoleAndInitPlanet方法功能:

    • 5.1. 從控制台讀取一個String參數

    • 5.2. 如果參數等於其中之一行星接口的常量,創建合適的thePlanet對象。

仔細閱讀任務條件後,我們可以清楚地看到為什麼這裡需要一個Singleton 。事實上,我們被要求為以下每個類創建一個實例:SunMoonEarth。假設我們應該創造不超過一個太陽/月亮/地球是有道理的。否則,我們會陷入荒謬的境地,除非你正在編寫你的星球大戰版本。三步實現Java中的Singleton模式 在Java中,Singleton行為不能使用普通的構造器來實現,因為構造器總是返回一個新的對象。因此, Singleton的所有實現歸結為隱藏構造函數,創建一個控制單例對像生命週期的公共靜態方法,並“銷毀”所有新出現的對象。如果訪問單例,它應該創建一個新對象(如果程序中尚不存在),或者返回一個現有對象。要做到這一點:
  1. 您需要為該類提供一個存儲單個對象的私有靜態字段:

    
    public class LazyInitializedSingleton {
    	private static LazyInitializedSingleton instance; // #1
    }
    
  2. 將(默認)構造函數設為私有。這意味著它不能在類外訪問,也不能返回新對象:

    
    public class LazyInitializedSingleton {
    	private static LazyInitializedSingleton instance;
    private LazyInitializedSingleton(){} // #2
    } 
    
  3. 聲明將用於獲取單例的靜態創建方法:

    
    public class LazyInitializedSingleton {
        private static LazyInitializedSingleton instance;
            private LazyInitializedSingleton() {}
            public static LazyInitializedSingleton getInstance() { // #3
            if (instance == null) { // If the object has not yet been created
                instance = new LazyInitializedSingleton(); // Create a new object
            }
            return instance; // Return the previously created object
        }
    }
    
上面的例子有些笨拙,因為我們只是隱藏了構造函數並提供了我們自己的方法而不是標準構造函數。由於本文旨在確保 CodeGym 學生接觸到這種模式(以及一般的設計模式),因此此處不會描述更複雜的單例實現的細微差別。我們只注意到,根據程序的複雜性,這種模式可能需要進一步細化。例如,在多線程環境中(請參閱有關線程的文章),多個不同的線程可能同時訪問單例方法,並且上述代碼將停止工作,因為每個單獨的線程都可以創建該類的一個實例。因此,仍然有幾種不同的方法來創建適當的線程安全單例。但那是另一個故事 =)

最後……船長問的這個惰性初始化是什麼?

惰性初始化也稱為延遲初始化。這是一種編程技巧,其中資源密集型操作(創建對像是資源密集型操作)是按需執行的,而不是提前執行的。那麼在我們的Singleton Java 代碼中究竟發生了什麼?換句話說,我們的對像是在訪問時創建的,而不是提前創建的。您不應該假設惰性初始化以某種方式嚴格依賴於單例模式。延遲初始化也用於其他創建型設計模式,例如 Proxy 和 Factory Method,但這也是另一回事 =)
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION