C++面試寶典--設計模式

原文鏈接:https://www.nowcoder.com/tutorial/93/2f895548adc24f0b88ffcb01c7973f23

1. 請問你用過哪些設計模式,介紹一下單例模式的多線程安全問題

常見的設計模式如下:
單例模式:
(1)概念
單例模式主要解決一個全局使用的類頻繁的創建和銷燬的問題。單例模式下可以確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。單例模式有三個要素:一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。
(2)實現
C++的實現有兩種,一種通過局部靜態變量,利用其只初始化一次的特點,返回對象。另外一種,則是定義全局的指針,getInstance判斷該指針是否爲空,爲空時才實例化對象。
(3)優缺點
優點:在內存裏只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷燬實例(比如管理學院首頁頁面緩存)、避免對資源的多重佔用(比如寫文件操作)。
缺點:沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。
(4)使用場景
使用場景:要求生產唯一序列號、WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來、創建的一個對象需要消耗的資源過多,比如 I/O 與數據庫的連接等。

工廠模式:
(1)概念
工廠模式主要解決接口選擇的問題。該模式下定義一個創建對象的接口,讓其子類自己決定實例化哪一個工廠類,使其創建過程延遲到子類進行。
(2) 優缺點
解耦,代碼複用,更改功能容易。

觀察者模式:
(1)概念
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。觀察者模式中分爲觀察者和被觀察者,當被觀察者發生裝填改變時,觀察者會受到通知。主要爲了解決對象狀態改變給其他對象通知的問題,其實現類似於觀察者在被觀察者那註冊了一個回調函數。

裝飾器模式:
(1)概念
對已經存在的某些類進行裝飾,以此來擴展一些功能,從而動態的爲一個對象增加新的功能。裝飾器模式是一種用於代替繼承的技術,無需通過繼承增加子類就能擴展對象的新功能。使用對象的關聯關係代替繼承關係,更加靈活,同時避免類型體系的快速膨脹。
(2)優缺點
優點:裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。
缺點:多層裝飾比較複雜。
(3)使用場景
使用場景:擴展一個類的功能、動態增加功能,動態撤銷。

單例模式的多線程安全問題:
在單例模式的實現中,如果不採取任何措施,在多線程下是不安全的,可能會同時創建多個實例。
解決方法是:將該類的構造方法定義爲私有方法,這樣其他處的代碼就無法通過調用該類的構造方法來實例化該類的對象,只有通過該類提供的靜態方法來得到該類的唯一實例;在該類內提供一個靜態方法,當我們調用這個方法時,如果類持有的引用不爲空就返回這個引用,如果類保持的引用爲空就創建該類的實例並將實例的引用賦予該類保持的引用。因此,爲了保證單例模式在多線程下的線程安全,一般採用下面幾種方式實現單例模式:
(1)餓漢式:基於class loader機制避免多線程的同步問題,不過,instance在類裝載時就實例化,可能會產生垃圾對象。

public class Singleton{
	private static Singleton sin = new Singleton(); // 直接初始化一個實例對象
	private Singleton(){}; // private類型的構造函數保證其他類對象不能直接new一個該對象實例
	public static Singleton getSin(){ return sin; } // 該類唯一的一個public方法
};

(2)懶漢式:通過雙重鎖機制實現線程安全。

public class Singleton{
	private static Singleton instance;
	private Singleton(){};
	// 對獲取實例的方法進行同步
	public static Singleton getInstance(){ 
		if (instance == null){
			synchronized( Singleton.class){
				if (instance == null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
};

2. 請你說一說OOP的設計模式的五項原則

(1)單一職責原則
單一職責有2個含義,一個是避免相同的職責分散到不同的類中,另一個是避免一個類承擔太多職責。減少類的耦合,提高類的複用性。
(2)接口隔離原則
表明客戶端不應該被強迫實現一些他們不會使用的接口,應該把胖接口中額方法分組,然後用多個接口代替它,每個接口服務於一個子模塊。簡單說,就是使用多個專門的接口比使用單個接口好很多。
(3)開放-封閉原則
open模塊的行爲必須是開放的、支持擴展的,而不是僵化的。
closed在對模塊的功能進行擴展時,不應該影響或大規模影響已有的程序模塊。一句話概括:一個模塊在擴展性方面應該是開放的而在更改性方面應該是封閉的。
核心思想就是對抽象編程,而不對具體編程。
(4)替換原則
子類型必須能夠替換掉他們的父類型、並出現在父類能夠出現的任何地方。
(5)依賴倒置原則
上層模塊不應該依賴於下層模塊,他們共同依賴於一個抽象,即:父類不能依賴子類,他們都要依賴抽象類。
抽象不能依賴於具體,具體應該要依賴於抽象。

主要針對繼承的設計原則
(1)父類的方法都要在子類中實現或者重寫,並且派生類只實現其抽象類中生命的方法,而不應當給出多餘的,方法定義或實現。
(2)在客戶端程序中只應該使用父類對象而不應當直接使用子類對象,這樣可以實現運行期間綁定。

3. 單例模式中的懶漢加載,如果併發訪問該怎麼做?

使用鎖機制,防止多次訪問,可以這樣,第一次判斷爲空不加鎖,若爲空,再進行加鎖判斷是否爲空,若爲空則生成對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章