中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

php網(wǎng)站后臺模板推廣app最快的方法

php網(wǎng)站后臺模板,推廣app最快的方法,怎么用手機(jī)自己做網(wǎng)站,pageadmin cms官網(wǎng)聲明:僅為個人學(xué)習(xí)總結(jié),還請批判性查看,如有不同觀點(diǎn),歡迎交流。 摘要 《Head First設(shè)計模式》第5章筆記:結(jié)合示例應(yīng)用和代碼,介紹單例模式,包括遇到的問題、采用的解決方案、以及達(dá)到的效果?!?article class="baidu_pl">

聲明:僅為個人學(xué)習(xí)總結(jié),還請批判性查看,如有不同觀點(diǎn),歡迎交流。

摘要

《Head First設(shè)計模式》第5章筆記:結(jié)合示例應(yīng)用和代碼,介紹單例模式,包括遇到的問題、采用的解決方案、以及達(dá)到的效果。

目錄

  • 摘要
  • 1 示例應(yīng)用
  • 2 引入設(shè)計模式
    • 2.1 私有構(gòu)造方法
    • 2.2 靜態(tài)方法
    • 2.3 靜態(tài)變量
    • 2.4 經(jīng)典單例實現(xiàn)
    • 2.5 單例模式定義
    • 2.6 第1版改進(jìn)
  • 3 遇到問題
    • 3.1 多線程方案1:同步方法
    • 3.2 多線程方案2:急切實例化
    • 3.3 多線程方案3:雙重檢查鎖定
    • 3.4 其它注意事項
    • 3.5 使用枚舉
  • 4 示例代碼
    • 4.1 Java 示例
    • 4.2 C++11 示例
  • 5 設(shè)計工具箱
    • 5.1 OO 基礎(chǔ)
    • 5.2 OO 原則
    • 5.3 OO 模式
  • 參考


1 示例應(yīng)用

示例應(yīng)用是巧克力工廠的鍋爐控制系統(tǒng)。巧克力鍋爐將巧克力和牛奶混合,加熱至沸騰,然后將它們送到制作巧克力棒的下一階段。

鍋爐的狀態(tài)包括 boolean empty(是否為空)和 boolean boiled(是否沸騰),相應(yīng)的狀態(tài)轉(zhuǎn)換情況如下:

初始
加滿
fill
煮沸
boil
排出
drain
結(jié)束

未沸
滿
未沸
滿
沸騰

下面是巧克力鍋爐的定義,在執(zhí)行 fill()、boil()drain() 操作時,都進(jìn)行了嚴(yán)格的狀態(tài)判斷,防止出現(xiàn)鍋爐空燒、排出未煮沸巧克力等糟糕情況。

public class ChocolateBoiler {private boolean empty;private boolean boiled;public ChocolateBoiler() {empty = true;boiled = false;}public void fill() {if (isEmpty()) { // 狀態(tài)為空時,才可以執(zhí)行操作System.out.println("向鍋爐中加滿牛奶和巧克力");empty = false;boiled = false;}}public void drain() {if (!isEmpty() && isBoiled()) { // 狀態(tài)為滿并且沸騰時,才可以執(zhí)行操作System.out.println("從鍋爐中排出牛奶和巧克力");empty = true;boiled = false;}}public void boil() {if (!isEmpty() && !isBoiled()) { // 狀態(tài)為滿并且未沸騰時,才可以執(zhí)行操作System.out.println("將鍋爐中的牛奶和巧克力煮沸");boiled = true;}}public boolean isEmpty() { return empty; }public boolean isBoiled() { return boiled; }
}

除了狀態(tài)監(jiān)控,為保證系統(tǒng)正常運(yùn)行,還要避免為一臺鍋爐創(chuàng)建多個 ChocolateBoiler 實例。否則,由它們共同操作鍋爐,情況也會很糟糕。

2 引入設(shè)計模式

接下來,我們嘗試通過設(shè)計模式來確保一個類 ChocolateBoiler 只能創(chuàng)建單一實例(單例)。

顯然,通過 new ChocolateBoiler() 可以創(chuàng)建一個實例;但是,再次執(zhí)行 new ChocolateBoiler() 還會創(chuàng)建另一個實例。
所以,我們的目標(biāo)是讓 new ChocolateBoiler() 只能被執(zhí)行一次。

要達(dá)成目標(biāo),可以分為下面 3 個步驟。

2.1 私有構(gòu)造方法

MyClass 類為例。

思考題

下面哪個或哪些選項可以將 MyClass 類實例化?【答案在第 20 行】public class MyClass {// ...private MyClass() {}
}A. MyClass 的包外類
B. MyClass 的同包類
C. MyClass 的子類
D. MyClass 的內(nèi)部代碼
E. 沒有任何辦法答案:D
解析:一個類的 private 成員(包括構(gòu)造方法)只能從“它所在類的內(nèi)部”訪問。

達(dá)成目標(biāo)的第1步:定義私有構(gòu)造方法,阻止外部類直接執(zhí)行 new MyClass() 創(chuàng)建實例。

2.2 靜態(tài)方法

類的靜態(tài)成員(靜態(tài)方法和靜態(tài)變量),屬于類本身,而不屬于類的某個實例。

通過在類的內(nèi)部定義 getInstance() 方法,可以訪問類的私有構(gòu)造方法,創(chuàng)建實例。

public class MyClass {// ...private MyClass() {}public static MyClass getInstance() {return new MyClass();}
}

通過將 getInstance() 聲明為 static 靜態(tài)方法,能夠在不創(chuàng)建任何實例的情況下,直接使用類名訪問 getInstance()。

MyClass obj = MyClass.getInstance();

達(dá)成目標(biāo)的第2步:定義靜態(tài)方法,由類自身執(zhí)行 new MyClass() 創(chuàng)建實例,并對外提供獲取實例的統(tǒng)一接口。

2.3 靜態(tài)變量

靜態(tài)變量屬于類本身,用于存儲“類級別”的狀態(tài)或共享數(shù)據(jù)。由于它與類的任何實例都無關(guān),所以可以用來控制實例的創(chuàng)建。

通過在類的內(nèi)部定義靜態(tài)變量 uniqueInstance,控制 MyClass 只能創(chuàng)建單一實例(單例)。

public class MyClass {private static MyClass uniqueInstance;  // 在類加載時被初始化為 nullprivate MyClass() {}public static MyClass getInstance() {if (uniqueInstance == null) {       // 在還沒有創(chuàng)建任何實例的情況下,可以創(chuàng)建實例,確保實例的唯一性uniqueInstance = new MyClass(); // 使用 uniqueInstance 引用該實例}return uniqueInstance;              // 返回已經(jīng)創(chuàng)建的實例}
}

達(dá)成目標(biāo)的第3步:定義靜態(tài)變量,引用類的唯一實例,限制 new MyClass() 只能被執(zhí)行一次。

2.4 經(jīng)典單例實現(xiàn)

我們使用一個私有構(gòu)造方法、一個靜態(tài)方法、一個靜態(tài)變量,實現(xiàn)了經(jīng)典的單例模式。

public class Singleton {// 一個靜態(tài)變量(私有),引用 Singleton 的唯一實例private static Singleton uniqueInstance;// 在這里添加其它有用的變量// 將構(gòu)造方法聲明為私有的,只有 Singleton 可以實例化這個類private Singleton() {// ...}// 一個靜態(tài)方法(公共),用于創(chuàng)建唯一的 Singleton 實例,并將其返回public static Singleton getInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;}// 在這里添加其它有用的方法// 當(dāng)然,Singleton 是一個正常的類,會有一些實現(xiàn)相應(yīng)功能的變量和方法
}

在單例模式中:

  • 由一個類來管理自己的唯一實例,要想訪問實例,只能通過該類;
  • 在需要訪問實例時,只需要調(diào)用該類提供的靜態(tài)方法(即,該實例的全局訪問點(diǎn))。

2.5 單例模式定義

單例模式(Singleton Pattern)
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點(diǎn)。
Ensure a class only has one instance, and provide a global point of access to it.

Singleton
-Singleton uniqueInstance
-usefulSingletonData
-Singleton()
+getInstance() : Singleton
+usefulSingletonMethod()
  1. 私有(或保護(hù))構(gòu)造函數(shù) Singleton(),確保外部無法直接實例化;
  2. 私有(或保護(hù))靜態(tài)成員變量 uniqueInstance,引用 Singleton 的唯一實例;
  3. 公共靜態(tài)成員方法 getInstance(),提供訪問 Singleton 唯一實例的全局接口;
  4. 業(yè)務(wù)相關(guān)的數(shù)據(jù) usefulSingletonData 和方法 usefulSingletonMethod(),用于實現(xiàn)類的具體功能。

單例模式的優(yōu)點(diǎn):

  1. 控制實例訪問
    通過 getInstance() 方法,可以嚴(yán)格控制實例的訪問時機(jī)和訪問方式。
  2. 相比全局變量的優(yōu)勢
    • 實例唯一性:單例可以保證實例唯一,而全局變量無法保證;
    • 延遲初始化:單例可以根據(jù)需要創(chuàng)建實例,而全局變量在程序啟動時就會創(chuàng)建(無論是否會用到);
    • 命名空間占用:單例的實例被封裝在類內(nèi)部,而全局變量直接占用全局命名空間(命名空間污染)。
  3. 實例數(shù)量可變
    通過修改 2.3 靜態(tài)變量 的實現(xiàn),也可以讓 Singleton 類管理自己的多個實例。

延伸閱讀:《設(shè)計模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》 3.5 Singleton(單件)— 對象創(chuàng)建型模式 [P96-102]

2.6 第1版改進(jìn)

采用單例模式,已經(jīng)實現(xiàn)讓一個類只能創(chuàng)建單一實例。下面是改進(jìn)后的 ChocolateBoiler 類:

public class ChocolateBoiler {private boolean empty;private boolean boiled;// 增加靜態(tài)變量、靜態(tài)方法,修改構(gòu)造方法為私有private static ChocolateBoiler uniqueInstance;private ChocolateBoiler() {empty = true;boiled = false;}public static ChocolateBoiler getInstance() {if (uniqueInstance == null) {uniqueInstance = new ChocolateBoiler();}return uniqueInstance;}// 后面的代碼沒有變化public void fill() {if (isEmpty()) { // 狀態(tài)為空時,才可以執(zhí)行操作System.out.println("向鍋爐中加滿牛奶和巧克力");empty = false;boiled = false;}}public void drain() {if (!isEmpty() && isBoiled()) { // 狀態(tài)為滿并且沸騰時,才可以執(zhí)行操作System.out.println("從鍋爐中排出牛奶和巧克力");empty = true;boiled = false;}}public void boil() {if (!isEmpty() && !isBoiled()) { // 狀態(tài)為滿并且未沸騰時,才可以執(zhí)行操作System.out.println("將鍋爐中的牛奶和巧克力煮沸");boiled = true;}}public boolean isEmpty() { return empty; }public boolean isBoiled() { return boiled; }
}

3 遇到問題

糟糕!在使用多線程對巧克力鍋爐控制器進(jìn)行優(yōu)化后,鍋爐發(fā)生了溢出!

鍋爐溢出

我們來查找一下問題的原因。

下面是鍋爐控制器 BoilerController 中的相關(guān)代碼:

ChocolateBoiler boiler = ChocolateBoiler.getInstance();
boiler.fill();   // 加滿
boiler.boil();   // 煮沸
boiler.drain();  // 排出

現(xiàn)在有兩個線程都需要執(zhí)行上述代碼:

  • 如果它們引用相同的 ChocolateBoiler 實例,就會共享一致的狀態(tài),并通過對狀態(tài)的嚴(yán)格監(jiān)控,讓鍋爐運(yùn)轉(zhuǎn)良好;(此處忽略數(shù)據(jù)競爭)
  • 如果它們引用不同的 ChocolateBoiler 實例,就會擁有各自的狀態(tài),如果一個實例已經(jīng)加滿鍋爐,而另一個實例還是空置狀態(tài),并繼續(xù)加入原料,就會導(dǎo)致鍋爐溢出。

可是,ChocolateBoiler 已經(jīng)定義為單例,這兩個線程還會引用不同的實例嗎?
我們來仔細(xì)分析一下 getInstance() 的定義,以及它可能的執(zhí)行時序。

public static ChocolateBoiler getInstance() {if (uniqueInstance == null) {uniqueInstance = new ChocolateBoiler();}return uniqueInstance;
}
時序線程1線程2uniqueInstance
1ChocolateBoiler.getInstance()null
2if (uniqueInstance == null)null
3掛起ChocolateBoiler.getInstance()null
4if (uniqueInstance == null)null
5uniqueInstance = new ChocolateBoiler();object1
6return uniqueInstance;object1
7uniqueInstance = new ChocolateBoiler();object2
8return uniqueInstance;object2

在多線程環(huán)境下,果然有可能創(chuàng)建兩個 ChocolateBoiler 實例 object1object2。

為了確保實例的創(chuàng)建是線程安全的,我們有多種可選方案,包括同步方法、急切實例化、雙重檢查鎖定等。

3.1 多線程方案1:同步方法

只要把 getInstance() 變成同步方法(添加 synchronized 關(guān)鍵字),就可以保證實例創(chuàng)建的線程安全性。

Java 概念:內(nèi)在鎖(intrinsic lock)
每個 Java 對象都有一個內(nèi)在鎖,獲得對象的內(nèi)在鎖就能夠獨(dú)占該對象的訪問權(quán),試圖訪問被鎖定對象的線程將被阻塞,直到持有該鎖的線程釋放鎖。使用 synchronized 關(guān)鍵字可以獲得對象的內(nèi)在鎖。

Java 概念:方法同步(Method Synchronization)

  • 當(dāng)一個線程調(diào)用一個對象的非靜態(tài) synchronized 方法時,它會在方法執(zhí)行之前,自動嘗試獲得該對象的內(nèi)在鎖;在方法返回之前,線程一直持有鎖。
  • 一旦某個線程鎖定了某個對象,其他線程就不能執(zhí)行同一個對象的“同一方法或其他同步方法”,只能阻塞等待,直到這個鎖再次變成可用的為止。
  • 鎖還可以重入(reentrant),這意味著持有鎖的線程可以調(diào)用同一對象上的其他同步方法;當(dāng)最外層同步方法返回時,會釋放該對象的內(nèi)在鎖。
  • 靜態(tài)方法也可以同步,在這種情況下,會使用與該方法的類關(guān)聯(lián)的 Class 對象的鎖(每個類都有一個對應(yīng)的 Class 對象,如 Singleton.class,包含該類的元數(shù)據(jù))。
public class Singleton {private static Singleton uniqueInstance;private Singleton() {}// synchronized 保證沒有兩個線程可以同時執(zhí)行 getInstance()// 一旦某個線程開始執(zhí)行 getInstance(),就會獲得鎖;其它線程再調(diào)用 getInstance(),會阻塞等待,直到持有鎖的線程釋放鎖public static synchronized Singleton getInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;}public String getDescription() { return "I'm a thread safe Singleton!"; }
}

同步方法實現(xiàn)簡單,但是也有明顯的缺點(diǎn):

  1. 運(yùn)行時開銷大:同步一個方法可能會使性能下降 100 倍(synchronizing a method can decrease performance by a factor of 100);
  2. 存在不必要的資源浪費(fèi):實際上,只有第一次執(zhí)行 getInstance() 創(chuàng)建實例時,才需要同步;然而,現(xiàn)在每次執(zhí)行 getInstance() 都需要同步。

不過,如果 getInstance() 的性能對應(yīng)用來說并不重要,那么使用同步方法也沒有問題。

3.2 多線程方案2:急切實例化

在調(diào)用 getInstance() 時創(chuàng)建實例,被稱為延遲初始化(Lazy Initialization);
與之相對的,可以在類加載時直接創(chuàng)建實例,即急切初始化(Eager Initialization);因為類加載具有線程安全性,所以實例的創(chuàng)建也是線程安全的。

public class Singleton {// 在類加載時直接創(chuàng)建實例,初始化靜態(tài)變量private static Singleton uniqueInstance = new Singleton();private Singleton() {}// 直接返回已經(jīng)創(chuàng)建的實例public static Singleton getInstance() {return uniqueInstance;}public String getDescription() { return "I'm a thread safe Singleton!"; }
}

急切實例化的特點(diǎn)和相關(guān)影響如下:

  • 特點(diǎn):一定會創(chuàng)建實例;
    影響:如果應(yīng)用有可能不使用實例,那么會造成不必要的資源浪費(fèi)。
  • 特點(diǎn):在應(yīng)用啟動時就會創(chuàng)建實例;
    影響:如果實例的創(chuàng)建或運(yùn)行比較消耗資源,那么會給應(yīng)用帶來一定的負(fù)擔(dān)。

如果應(yīng)用需要盡早的使用實例,或者即便使用的時間比較晚,但實例的創(chuàng)建和運(yùn)行負(fù)擔(dān)并不重,那么也可以選擇急切實例化。

3.3 多線程方案3:雙重檢查鎖定

雙重檢查鎖定(Double-Checked Locking):先檢查實例是否已經(jīng)創(chuàng)建,如果尚未創(chuàng)建,才進(jìn)行同步。
以此減少 同步方法 中 getInstance() 對同步的使用。(需要 Java 5 及以后版本)

Java 概念:塊同步(Block Synchronization)
Java 允許使用 synchronized 關(guān)鍵字來鎖定任何對象,從而實現(xiàn)代碼塊的同步。
synchronized(object) { // 在 object 被鎖定的情況下執(zhí)行某些操作 }
在塊中的代碼執(zhí)行之后,鎖會被釋放。

Java 概念:可見性(Visibility)
在一個單線程程序中,讀取變量的值總是會得到最后寫入該變量的值。但是,在 Java 的多線程應(yīng)用程序中,一個線程可能看不到另一個線程所做的更改,除非在數(shù)據(jù)上執(zhí)行的操作是同步的。然而,同步是有代價的。如果想要的是可見性,而不需要互斥,那么可以使用 volatile 關(guān)鍵字,該關(guān)鍵字可以確保當(dāng)一個線程修改了變量的值之后,新值對于其他線程立即可見。

Java 概念:指令重排序(Instruction Reordering)
編譯器或處理器為了優(yōu)化程序性能,可能會改變指令的執(zhí)行順序。
例如對于 uniqueInstance = new Singleton(); 語句,其包含的步驟示意如下:

  1. memory = allocate(sizeof(Singleton.class)); 在堆內(nèi)存中為對象分配空間
  2. construct(memory, Singleton.class); 在分配的內(nèi)存空間上調(diào)用構(gòu)造函數(shù)來初始化對象
  3. uniqueInstance = memory; 將對象引用指向分配的內(nèi)存空間

在進(jìn)行指令重排序后,步驟3可能會在步驟2之前執(zhí)行。如果另一個線程在“步驟3之后、步驟2之前”訪問 uniqueInstance,就會獲取到一個未完全初始化的實例。通過將 uniqueInstance 聲明為 volatile,可以禁止這種重排序。這樣,當(dāng) uniqueInstance 不為 null 時,它所引用的實例就是完全初始化的。

public class Singleton {// 將 uniqueInstance 聲明為 volatile,確保其可見性,并禁止指令重排序private volatile static Singleton uniqueInstance;private Singleton() {}public static Singleton getInstance() {if (uniqueInstance == null) {                 // 【第一次檢查】實例是否創(chuàng)建,只有尚未創(chuàng)建,才進(jìn)入同步塊;synchronized (Singleton.class) {          // 嘗試加鎖,如果其它線程已經(jīng)加鎖,則阻塞等待;if (uniqueInstance == null) {         // 成功加鎖后,【再次檢查】實例是否尚未創(chuàng)建,uniqueInstance = new Singleton(); // 因為在阻塞等待的過程中,其它線程可能已經(jīng)創(chuàng)建實例。}}}return uniqueInstance;}public String getDescription() { return "I'm a thread safe Singleton!"; }
}

如果使用同步方法存在性能問題,那么使用雙重檢查鎖定,則可以兼顧性能和線程安全。

3.4 其它注意事項

除了創(chuàng)建實例時的線程安全問題,在 java 中,使用單例還有一些其它注意事項:

  1. 類加載器問題
    • 問題描述:如果有兩個或多個類加載器,就可以多次加載同一個類(每個類加載器一次)。如果這個類剛好是一個單例,就會有多于一個的實例。
    • 解決辦法:確保單例通過同一個類加載器加載,通常使用系統(tǒng)類加載器(即啟動類加載器)加載單例。
  2. 反射問題
    • 問題描述:通過反射可以調(diào)用類的私有構(gòu)造方法,因此可能會創(chuàng)建類的多個實例。
    • 解決辦法:在構(gòu)造方法中添加防御性代碼,防止通過反射創(chuàng)建多個實例。例如,可以在構(gòu)造方法中檢查是否已經(jīng)存在實例,如果存在則拋出異常。
  3. 序列化和反序列化問題
    • 問題描述:當(dāng)單例實現(xiàn)了 Serializable 接口時,序列化會將對象的狀態(tài)保存下來,之后反序列化可以重建對象,這樣可能會創(chuàng)建類的多個實例。
    • 解決辦法:在單例中添加一個 readResolve 方法,該方法在反序列化時會被調(diào)用,可以返回單例實例,而不是創(chuàng)建一個新的實例。

3.5 使用枚舉

Java 概念:枚舉(Enum)
在 Java 中,枚舉是一種特殊的類,是 java.lang.Enum 的子類,它的特性包括:

  • 枚舉類默認(rèn)具有私有的構(gòu)造方法,而且不允許顯式定義非私有構(gòu)造方法,因此無法從外部實例化;并且也不能通過反射來訪問構(gòu)造方法;
  • 枚舉實例在類被加載到 JVM 時靜態(tài)初始化,保證了實例的唯一性和線程安全性;
  • 枚舉類在序列化和反序列化的過程中,會由 JVM 保證枚舉實例的唯一性;
  • 每個枚舉常量自動被視為 public static final,并且是枚舉類型的一個實例;
  • 枚舉類也可以定義自己的方法和變量。

因為 Java 會保證枚舉類中每個枚舉常量的唯一性,所以通過定義一個包含單個枚舉常量的枚舉類,就可以自然地實現(xiàn)單例模式。

public enum Singleton {UNIQUE_INSTANCE;// 可以添加有用的變量和方法public String getDescription() {return "I'm a thread safe Singleton!";}
}

枚舉的使用:

Singleton singleton = Singleton.UNIQUE_INSTANCE;
System.out.println(singleton.getDescription());

使用枚舉實現(xiàn)單例,代碼簡潔明了,而且可以避免前文提到的所有單例問題,包括創(chuàng)建實例時的線程安全、類加載問題、反射問題、以及序列化和反序列化問題。因此,枚舉是實現(xiàn)單例模式的一種推薦方式。

4 示例代碼

4.1 Java 示例

雙重檢查鎖定方式:

// ChocolateBoiler.java
public class ChocolateBoiler {private boolean empty;private boolean boiled;private volatile static ChocolateBoiler uniqueInstance;private ChocolateBoiler() {empty = true;boiled = false;System.out.println("[" + Thread.currentThread().getName() + "] 創(chuàng)建巧克力鍋爐實例,初始狀態(tài):空、未沸");}public static ChocolateBoiler getInstance() {if (uniqueInstance == null) {synchronized (ChocolateBoiler.class) {if (uniqueInstance == null) {uniqueInstance = new ChocolateBoiler();}}}System.out.println("[" + Thread.currentThread().getName() + "] 返回巧克力鍋爐實例,當(dāng)前狀態(tài):" + (uniqueInstance.isEmpty() ? "空" : "滿") + "、" + (uniqueInstance.isBoiled() ? "沸騰" : "未沸"));return uniqueInstance;}public synchronized void fill() {System.out.println("[" + Thread.currentThread().getName() + "] 嘗試加滿,當(dāng)前狀態(tài):" + (isEmpty() ? "空" : "滿") + "、" + (isBoiled() ? "沸騰" : "未沸"));if (isEmpty()) {System.out.println(" => 向鍋爐中加滿牛奶和巧克力");empty = false;boiled = false;}}public synchronized void drain() {System.out.println("[" + Thread.currentThread().getName() + "] 嘗試排出,當(dāng)前狀態(tài):" + (isEmpty() ? "空" : "滿") + "、" + (isBoiled() ? "沸騰" : "未沸"));if (!isEmpty() && isBoiled()) {System.out.println(" => 從鍋爐中排出牛奶和巧克力");empty = true;boiled = false;}}public synchronized void boil() {System.out.println("[" + Thread.currentThread().getName() + "] 嘗試煮沸,當(dāng)前狀態(tài):" + (isEmpty() ? "空" : "滿") + "、" + (isBoiled() ? "沸騰" : "未沸"));if (!isEmpty() && !isBoiled()) {System.out.println(" => 將鍋爐中的牛奶和巧克力煮沸");boiled = true;}}public synchronized boolean isEmpty() { return empty; }public synchronized boolean isBoiled() { return boiled; }
}

枚舉方式:

// ChocolateBoilerEnum.java
public enum ChocolateBoilerEnum {UNIQUE_INSTANCE;private boolean empty;private boolean boiled;private ChocolateBoilerEnum() {empty = true;boiled = false;System.out.println("[" + Thread.currentThread().getName() + "] 創(chuàng)建巧克力鍋爐實例,初始狀態(tài):空、未沸");}public synchronized void fill() { /* 代碼相同 */ }public synchronized void drain() { /* 代碼相同 */ }public synchronized void boil() { /* 代碼相同 */ }public synchronized boolean isEmpty() { /* 代碼相同 */ }public synchronized boolean isBoiled() { /* 代碼相同 */ }
}

測試代碼:

// BoilerController.java
public class BoilerController {public static void main(String args[]) {Runnable boilerTask = new Runnable() {@Overridepublic void run() {ChocolateBoiler boiler = ChocolateBoiler.getInstance();// ChocolateBoilerEnum boiler = ChocolateBoilerEnum.UNIQUE_INSTANCE;boiler.fill();   // 加滿boiler.boil();   // 煮沸boiler.drain();  // 排出}};Thread thread1 = new Thread(boilerTask);Thread thread2 = new Thread(boilerTask);thread1.start();thread2.start();}
}

4.2 C++11 示例

4.2.1 Meyers Singleton

Meyers Singleton 是一種在 C++ 中實現(xiàn)單例模式的簡潔方法,由 Scott Meyers(Effective C++ 的作者)提出。

struct Singleton {static Singleton& getInstance() {static Singleton uniqueInstance; return uniqueInstance;}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:Singleton() = default; 
};

這種方法基于 C++11 的保證,即局部靜態(tài)對象會在“函數(shù)第一次執(zhí)行到該對象的定義處”時被初始化(同時具有線程安全性)。作為額外的好處,如果從未調(diào)用“模擬非局部靜態(tài)對象”的函數(shù)(即getInstance()函數(shù),使用在函數(shù)內(nèi)部定義的“局部靜態(tài)對象”,模擬在全局/命名空間范圍內(nèi)定義的“非局部靜態(tài)對象”),就永遠(yuǎn)不會產(chǎn)生構(gòu)造和析構(gòu)該對象的開銷。
Scott Myers says: “This approach is founded on C++'s guarantee that local static objects are initialized when the object’s definition is first encountered during a call to that function.” … “As a bonus, if you never call a function emulating a non-local static object, you never incur the cost of constructing and destructing the object.”

4.2.2 ChocolateBoiler

局部靜態(tài)變量方式(Meyers Singleton):

struct ChocolateBoiler {static ChocolateBoiler& getInstance() {// 對于局部靜態(tài)變量,由 C++11 保證只在第一次執(zhí)行到變量定義處時,進(jìn)行初始化,并且是線程安全的static ChocolateBoiler uniqueInstance;std::cout << "[" << std::this_thread::get_id() << "] 返回巧克力鍋爐實例,當(dāng)前狀態(tài):" << (uniqueInstance.isEmpty() ? "空" : "滿") << "、" << (uniqueInstance.isBoiled() ? "沸騰" : "未沸") << '\n';return uniqueInstance;}void fill() {std::lock_guard<std::recursive_mutex> guard(mtx);std::cout << "[" << std::this_thread::get_id() << "] 嘗試加滿,當(dāng)前狀態(tài):" << (isEmpty() ? "空" : "滿") << "、" << (isBoiled() ? "沸騰" : "未沸") << '\n';if (isEmpty()) {std::cout << " => 向鍋爐中加滿牛奶和巧克力\n";empty = false;boiled = false;}}void drain() {std::lock_guard<std::recursive_mutex> guard(mtx);std::cout << "[" << std::this_thread::get_id() << "] 嘗試排出,當(dāng)前狀態(tài):" << (isEmpty() ? "空" : "滿") << "、" << (isBoiled() ? "沸騰" : "未沸") << '\n';if (!isEmpty() && isBoiled()) {std::cout << " => 從鍋爐中排出牛奶和巧克力\n";empty = true;boiled = false;}}void boil() {std::lock_guard<std::recursive_mutex> guard(mtx);std::cout << "[" << std::this_thread::get_id() << "] 嘗試煮沸,當(dāng)前狀態(tài):" << (isEmpty() ? "空" : "滿") << "、" << (isBoiled() ? "沸騰" : "未沸") << '\n';if (!isEmpty() && !isBoiled()) {std::cout << " => 將鍋爐中的牛奶和巧克力煮沸\(zhòng)n";boiled = true;}}bool isEmpty() const {std::lock_guard<std::recursive_mutex> guard(mtx);return empty;}bool isBoiled() const {std::lock_guard<std::recursive_mutex> guard(mtx);return boiled;}ChocolateBoiler(const ChocolateBoiler&) = delete;ChocolateBoiler& operator=(const ChocolateBoiler&) = delete;private:ChocolateBoiler() : empty(true), boiled(false) {std::cout << "[" << std::this_thread::get_id() << "] 創(chuàng)建巧克力鍋爐實例,初始狀態(tài):空、未沸\(zhòng)n";}bool empty;bool boiled;static std::recursive_mutex mtx;
};std::recursive_mutex ChocolateBoiler::mtx;

測試代碼:

#include <iostream>
#include <mutex>
#include <thread>// 在這里添加相關(guān)接口和類的定義int main() {std::function<void()> boilerTask = []() {ChocolateBoiler& boiler = ChocolateBoiler::getInstance();boiler.fill();boiler.boil();boiler.drain();};std::thread t1(boilerTask);std::thread t2(boilerTask);t1.join();t2.join();
}

4.2.3 一次性互斥

與 Java 的雙重檢查鎖定相比,C++11 提供了更為簡潔的“一次性互斥”機(jī)制。
通過 std::once_flag 類和 std::call_once() 函數(shù)模板來實現(xiàn)一次性互斥,確保即使有多個線程、多次、同時調(diào)用某個函數(shù)(可調(diào)用對象),其只會被執(zhí)行一次。復(fù)習(xí)回顧 線程池2-線程互斥 => 3.1.3 一次性互斥。

struct Singleton {static Singleton& getInstance() {std::call_once(initFlag, []() { uniqueInstance.reset(new Singleton()); });return *uniqueInstance;}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:Singleton() = default;static std::unique_ptr<Singleton> uniqueInstance;static std::once_flag initFlag;
};std::unique_ptr<Singleton> Singleton::uniqueInstance;
std::once_flag Singleton::initFlag;

5 設(shè)計工具箱

5.1 OO 基礎(chǔ)

OO 基礎(chǔ)回顧

  1. 抽象(Abstraction)
  2. 封裝(Encapsulation)
  3. 繼承(Inheritance)
  4. 多態(tài)(Polymorphism)

5.2 OO 原則

5.2.1 新原則

本章沒有介紹新的 OO 原則。

5.2.2 原則回顧

  1. 封裝變化。
    Encapsulate what varies.
  2. 針對接口編程,而不是針對實現(xiàn)編程。
    Program to interfaces, not implementations.
  3. 優(yōu)先使用組合,而不是繼承。
    Favor composition over inheritance.
  4. 盡量做到交互對象之間的松耦合設(shè)計。
    Strive for loosely coupled designs between objects that interact.
  5. 類應(yīng)該對擴(kuò)展開放,對修改關(guān)閉。
    Classes should be open for extension, but closed for modification.
  6. 依賴抽象,不依賴具體類。
    Depend on abstractions. Do not depend on concrete classes.

5.3 OO 模式

5.3.1 新模式

單例模式(Singleton Pattern)

  • 確保一個類只有一個實例,并提供一個全局訪問點(diǎn)。
    The Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.

5.3.2 模式回顧

1 創(chuàng)建型模式(Creational Patterns)

創(chuàng)建型模式與對象的創(chuàng)建有關(guān)。
Creational patterns concern the process of object creation.

  1. 工廠方法(Factory Method)
    • 定義了一個創(chuàng)建對象的接口,但由子類決定要實例化哪個類。
      The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate.
    • 工廠方法讓類把實例化推遲到子類。
      Factory Method lets a class defer instantiation to subclasses.
  2. 抽象工廠(Abstract Factory)
    • 提供一個接口,創(chuàng)建相關(guān)或依賴對象的家族,而不需要指定具體類。
      The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.

2 結(jié)構(gòu)型模式(Structural Patterns)

結(jié)構(gòu)型模式處理類或?qū)ο蟮慕M合。
Structural patterns deal with the composition of classes or objects.

  1. 裝飾者模式(Decorator Pattern)
    • 動態(tài)地給一個對象添加一些額外的職責(zé)。
      The Decorator Pattern attaches additional responsibilities to an object dynamically.
    • 就增加功能來說,裝飾者模式相比生成子類更為靈活。
      Decorators provide a flexible alternative to subclassing for extending functionality.

3 行為型模式(Behavioral Patterns)

行為型模式描述類或?qū)ο笾g的交互方式以及職責(zé)分配方式。
Behavioral patterns characterize the ways in which classes or objects interact and distribute responsibility.

  1. 策略模式(Strategy Pattern)
    • 定義一個算法家族,把其中的算法分別封裝起來,使得它們之間可以互相替換。
      Strategy defines a family of algorithms, encapsulates each one, and makes them interchangeable.
    • 讓算法的變化獨(dú)立于使用算法的客戶。
      Strategy lets the algorithm vary independently from clients that use it.
  2. 觀察者模式(Observer Pattern)
    • 定義對象之間的一對多依賴,
      The Observer Pattern defines a one-to-many dependency between objects
    • 這樣一來,當(dāng)一個對象改變狀態(tài)時,它的所有依賴者都會被通知并自動更新。
      so that when one object changes state, all of its dependents are notified and updated automatically.

參考

  1. [美]弗里曼、羅布森著,UMLChina譯.Head First設(shè)計模式.中國電力出版社.2022.2
  2. [美]伽瑪?shù)戎?李英軍等譯.設(shè)計模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ).機(jī)械工業(yè)出版社.2019.3
  3. wickedlysmart: Head First設(shè)計模式 Java 源碼
  4. [加]布迪·克尼亞萬著,沈澤剛譯.Java經(jīng)典入門指南.人民郵電出版社.2020.6

Hi, I’m the ENDing, nice to meet you here! Hope this article has been helpful.

http://www.risenshineclean.com/news/38451.html

相關(guān)文章:

  • 貴陽 網(wǎng)站建設(shè)網(wǎng)絡(luò)營銷主要內(nèi)容
  • 做旅行網(wǎng)站的依據(jù)及意義國內(nèi)十大搜索引擎網(wǎng)站
  • 福州專業(yè)網(wǎng)站建設(shè)友鏈交易網(wǎng)
  • app小程序開發(fā)價格網(wǎng)站優(yōu)化方式有哪些
  • 無錫網(wǎng)站建設(shè)制作方案網(wǎng)頁設(shè)計制作網(wǎng)站html代碼大全
  • 特效網(wǎng)站大全seo秘籍優(yōu)化課程
  • 哪家做網(wǎng)站便宜營銷型網(wǎng)站建站推廣
  • 網(wǎng)站開發(fā)數(shù)據(jù)庫課程設(shè)計專注于網(wǎng)站營銷服務(wù)
  • 做模擬人生類的游戲下載網(wǎng)站廣告開戶南京seo
  • 調(diào)查問卷在哪個網(wǎng)站做子域名在線查詢
  • 志勛網(wǎng)站建設(shè)公司中國十大外貿(mào)平臺
  • 建企業(yè)網(wǎng)站怎么做網(wǎng)站自然排名工具
  • 有域名怎樣做網(wǎng)站軟文網(wǎng)站發(fā)布平臺
  • 9元包郵網(wǎng)站怎么做seo搜索引擎優(yōu)化是什么
  • 網(wǎng)站域名建設(shè)費(fèi)進(jìn)什么科目人工智能培訓(xùn)心得體會
  • 有域名了怎么做網(wǎng)站百度推廣優(yōu)化公司
  • 新手做網(wǎng)站視頻講解大地seo視頻
  • 騰訊 網(wǎng)站開發(fā)如何在百度推廣自己的產(chǎn)品
  • 做網(wǎng)站運(yùn)營需要培訓(xùn)嗎在線搭建網(wǎng)站
  • 濰坊做網(wǎng)站的電話seo網(wǎng)站有優(yōu)化培訓(xùn)班嗎
  • 備案平臺新增網(wǎng)站優(yōu)秀的軟文廣告欣賞
  • h5互動網(wǎng)站建設(shè)今日百度小說排行榜
  • 易語言可以建設(shè)網(wǎng)站嗎新站seo優(yōu)化快速上排名
  • 網(wǎng)站開發(fā)實踐研究報告溫州seo排名公司
  • 給公眾號做頭像的網(wǎng)站北京百度關(guān)鍵詞優(yōu)化
  • 智能建站cms管理系統(tǒng)百度推廣關(guān)鍵詞優(yōu)化
  • 重慶網(wǎng)站租賃空間國內(nèi)新聞最新消息今天
  • asp網(wǎng)站仿制公司官網(wǎng)開發(fā)制作
  • 同程網(wǎng) 網(wǎng)站模板國外網(wǎng)絡(luò)推廣
  • 怎么建立一個網(wǎng)站當(dāng)站長松原今日頭條新聞