亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.430618.com 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

JAVA 是較典型的面向對象語言。如果說 C++ 是設計模式的發源地(GoF 的書使用 C++ 描述的),那么 Java 將設計模式發揚光大。設計模式,很多人可能工作中沒有用到,因為大部分人停留在寫面條式的業務代碼,從頭擼到尾,沒有設計可言。但實際上,只要你用心思考,這樣的場景下也是很有可能用上設計模式的。特別是,當系統復雜時,設計模式的作用會很明顯。

雖然 Go 語言并非完全的面向對象語言,只提供了部分面向對象的特性,但一些設計模式還是可以使用的。這個系列嘗試講解在 Go 中使用設計模式,同時給出 Java 對應的版本,進行對比學習。另外,我們的設計模式不會局限在 GoF 的 23 中設計模式之中。

在開始設計模式之前,有必要提一下面向對象的 SOLID 5 大設計原則:

名稱縮寫含義The Single Responsibility Principle(單一職責)S對象應該具有單一的職責。這也是 Unix 的設計哲學The Open/Closed Principle(開/閉原則)O對擴展開發,對修改關閉The Liskov Substitution Principle(里氏替換)L對象應該可以在不破壞系統的情況下被子對象替換The Interface Segregation Principle(接口隔離)I不應強迫任何客戶端依賴其不使用的方法The Dependency Inversion Principle(依賴倒轉)D高級模塊不應依賴于低級實現

遵循這樣的設計原則,你的系統會更好維護。

除了 SOLID 5 大設計原則,一些書上可能還會提到下面的設計原則:

  • 合成/聚合復用原則(Composite/Aggregate Reuse Principle):盡量使用合成/聚合,而不要使用繼承。這也是 Go 語言設計遵循的,基于此,Go 中沒有繼承。
  • 迪米特法則(LoD),又叫 最少知識原則:一個對象應當對其他對象有盡可能少的了解;一個軟件實體應當與盡可能少的其他實體發生相互作用。

在你日常的工作中,可以運用以上原則審視你的設計,改進你的設計。

今天先看第一個設計模式。

1、單例模式簡介

面向對象中的單例模式是一個常見、簡單的模式。

英文名稱:Singleton Pattern,該模式規定一個類只允許有一個實例,而且自行實例化并向整個系統提供這個實例。因此單例模式的要點有:1)只有一個實例;2)必須自行創建;3)必須自行向整個系統提供這個實例。

單例模式主要避免一個全局使用的類頻繁地創建與銷毀。當你想控制實例的數量,或有時候不允許存在多實例時,單例模式就派上用場了。

先看 Java 中的單例模式。

Go 和 Java 對比學習:單例模式

 

通過該類圖我們可以看出,實現一個單例模式有如下要求:

  • 私有、靜態的類實例變量;
  • 構造函數私有化;
  • 靜態工廠方法,返回此類的唯一實例;

根據實例化的時機,單例模式一般分成餓漢式和懶漢式。

  • 餓漢式:在定義 instance 時直接實例化,private static Singleton instance = new Singleton();
  • 懶漢式:在 getInstance 方法中進行實例化;

那兩者有什么區別或優缺點?餓漢式單例類在自己被加載時就將自己實例化。即便加載器是靜態的,餓漢式單例類被加載時仍會將自己實例化。單從資源利用率角度講,這個比懶漢式單例類稍差些。從速度和反應時間角度講,則比懶漢式單例類稍好些。然而,懶漢式單例類在實例化時,必須處理好在多個線程同時首次引用此類時的訪問限制問題,特別是當單例類作為資源控制器在實例化時必須涉及資源初始化,而資源初始化很有可能耗費時間。這意味著出現多線程同時首次引用此類的幾率變得較大。

2、單例模式的 Java 實現

結合上面的講解,以一個計數器為例,我們看看 Java 中餓漢式的實現:

public class Singleton {
  private static final Singleton instance = new Singleton();
  private int count = 0;
  private Singleton() {}
  public static Singleton getInstance() {
    return instance;
  }  public int Add() int {
    this.count++;
    return this.count;
  }}

代碼很簡單,不過多解釋。直接看懶漢式的實現:

public class Singleton {
  private static Singleton instance = null;
  private int count = 0;
  private Singleton() {}
  public static synchronized Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }    return instance;
  }  public int Add() int {
    this.count++;
    return this.count;
  }}

主要區別在于 getInstance 的實現,要注意 synchronized ,避免多線程時出現問題。

3、單例模式的 Go 實現

在 Go 語言中如何實現單例模式,類比 Java 代碼實現。

// 餓漢式單例模式
package singleton
type singleton struct {  count int
}var Instance = new(singleton)func (s *singleton) Add() int {
  s.count++  return s.count
}

前面說了,Go 只支持部分面向對象的特性,因此看起來有點不太一樣:

  • 類(結構體 singleton)本身非公開(小寫字母開頭,非導出);
  • 沒有提供導出的 GetInstance 工廠方法(Go 沒有靜態方法),而是直接提供包級導出變量 Instance;

這樣使用:

c := singleton.Instance.Add()

看看懶漢式單例模式在 Go 中如何實現:

// 懶漢式單例模式
package singleton
import ( "sync"
)type singleton struct {
  count int}var (  instance *singleton  mutex sync.Mutex)func New() *singleton {  mutex.Lock()  if instance == nil {
    instance = new(singleton)  }  mutex.Unlock()    return instance
}func (s *singleton) Add() int {  s.count++  return s.count
}

代碼多了不少:

  • 包級變量變成非導出(instance),注意這里類型應該用指針,因為結構體的默認值不是 nil;
  • 提供了工廠方法,按照 Go 的慣例,我們命名為 New();
  • 多 goroutine 保護,對應 Java 的 synchronized,Go 使用 sync.Mutex;

關于懶漢式有一個“雙重檢查”,這是 C 語言的一種代碼模式。

在上面 New() 函數中,同步化(鎖保護)實際上只在 instance 變量第一次被賦值之前才有用。在 instance 變量有了值之后,同步化實際上變成了一個不必要的瓶頸。如果能夠有一個方法去掉這個小小的額外開銷,不是更加完美嗎?因此出現了“雙重檢查”。看看 Go 如何實現“雙重檢查”,只看 New() 代碼:

func New() *singleton {
  if instance == nil { // 第一次檢查(①)
    // 這里可能有多于一個 goroutine 同時達到(②)
    mutex.Lock()
    // 這里每個時刻只會有一個 goroutine(③)
    if instance == nil { // 第二次檢查(④)
      instance = new(singleton)
    }
    mutex.Unlock()
  }
  
  return instance
}

有讀者可能看不懂上面代碼的意思,這里詳細解釋下。假設 goroutine X 和 Y 作為第一批調用者同時或幾乎同時調用 New 函數。

  1. 因為 goroutine X 和 Y 是第一批調用者,因此,當它們進入此函數時,instance 變量是 nil。因此 goroutine X 和 Y 會同時或幾乎同時到達位置 ①;
  2. 假設 goroutine X 會先達到位置 ②,并進入 mutex.Lock() 達到位置 ③。這時,由于 mutex.Lock 的同步限制,goroutine Y 無法到達位置 ③,而只能在位置 ② 等候;
  3. goroutine X 執行 instance = new(singleton) 語句,使得 instance 變量得到一個值,即對 singleton 實例的引用。此時,goroutine Y 只能繼續在位置 ② 等候;
  4. goroutine X 釋放鎖,返回 instance,退出 New 函數;
  5. goroutine Y 進入 mutex.Lock(),到達位置 ③,進而到達位置 ④。由于 instance 變量已經不是 nil,因此 goroutine Y 釋放鎖,返回 instance 所引用的 singleton 實例(也就是 goroutine X 鎖創建的 singleton 實例),退出 New 函數;

到這里,goroutine X 和 Y 得到了同一個 singleton 實例??梢娚厦娴?New 函數中,鎖僅用來避免多個 goroutine 同時實例化 singleton。

相比前面的版本,雙重檢查版本,只要 instance 實例化后,鎖永遠不會執行了,而前面版本每次調用 New 獲取實例都需要執行鎖。性能很顯然,我們可以基準測試來驗證:(雙重檢查版本 New 重命名為 New2)

package singleton_test
import ( "testing"
 "github.com/polaris1119/go-demo/singleton"
)func BenchmarkNew(b *testing.B) {
 for i := 0; i < b.N; i++ {
  singleton.New() }}func BenchmarkNew2(b *testing.B) {
 for i := 0; i < b.N; i++ {
  singleton.New2() }}

因為是單例,所以兩個基準測試需要分別執行。

New1 的結果:

$ go test -benchmem -bench ^BenchmarkNew$ github.com/polaris1119/go-demo/singleton
goos: darwin
goarch: amd64
pkg: github.com/polaris1119/go-demo/singleton
BenchmarkNew-8    80470467         14.0 ns/op        0 B/op        0 allocs/op
PASS
ok   github.com/polaris1119/go-demo/singleton 1.151s

New2 的結果:

$ go test -benchmem -bench ^BenchmarkNew2$ github.com/polaris1119/go-demo/singleton
goos: darwin
goarch: amd64
pkg: github.com/polaris1119/go-demo/singleton
BenchmarkNew2-8    658810392          1.80 ns/op        0 B/op        0 allocs/op
PASS
ok   github.com/polaris1119/go-demo/singleton 1.380s

New2 快十幾倍。

細心得讀者會發現,在 Go 中,餓漢式還有一種更好的實現方式,那就是使用 sync.Once,這是 Go 實現懶漢式更標準的做法。核心代碼如下(New3):

var once sync.Once
func New3() *singleton {    once.Do(func() {
        instance = new(singleton)
    })    return instance
}

通過基準測試,它的性能和 New2 差不多。

此外,無論是 Java 還是 Go,都有一些其他“黑魔法”,比如 Go 語言中,利用 init 函數來初始化唯一的單例。不過一般都不太建議,還是常規方式來。

Go 語言單例模式,一般推薦優先考慮使用餓漢式。但如果初始化比較耗時,懶漢式延遲初始化是更好的選擇。

4、使用場景

在 Go 語言中,如下兩個場景比較適合使用單例模式:

  • 數據庫實例。只想創建一個 DB 對象實例,該實例在整個應用程序中使用。
  • 日志實例。同樣,只創建一個 Logger 的實例,并且在整個應用程序中使用它。

參考資料

  • https://github.com/dwmkerr/hacker-laws

分享到:
標簽:語言
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定