設計模式學習-使用go實現代理模式

代理模式

定義

定義:為其對象提供一種代理以控制這個對象的訪問。通俗點講就是它在不改變原始類(或叫被代理類)程式碼的情況下,通過引入代理類來給原始類附加功能。

來點通俗的理解:

比如我們買火車票,除了火車站,很多代售點也是可以買的,代售點的作用就是代理模式

優點

1、代理模式在客戶端與目標對象之間起到一個中介作用和保護目標對象的作用;

2、代理對象可以擴展目標對象的功能;

3、代理模式能將客戶端與目標對象分離,在一定程度上降低了系統的耦合度,增加了程式的可擴展性;

缺點

1、代理模式會造成系統設計中類的數量增加;

2、在客戶端和目標對象之間增加一個代理對象,會造成請求處理速度變慢;

3、增加了系統的複雜度;

應用場景

  • 1、業務系統的非功能性需求開發

一些非功能性的業務需求,比如:監控、統計、鑒權、限流、事務、冪等、日誌。我們將這些附加功能與業務功能解耦,放到代理類中統一處理,讓程式設計師只需要關注業務方面的開發。

  • 2、代理模式在RPC中的應用

RPC框架也可以看成是一種代理模式。

GoF的《設計模式》一書中把它稱作遠程代理。通過遠程代理,將網路通訊、數據編解碼等細節隱藏起來。客戶端使用RPC服務就像使用本地函數一樣,RPC服務的開發者也只需要開發業務邏輯,就像開發本地使用的函數一樣,不需要關注跟客戶端的交互細節。

  • 3、代理模式在快取中的應用

對於一些介面的開發,有時候對於一個功能,我們會提供兩種介面,一種支援快取,一種不支援快取,對於需要實時數據的需求,我們讓其調用實時查詢介面,對於不需要實時數據的需求,我們讓其調用支援快取的介面。

簡單的做法就是介面寫兩個,一個支援快取的一個不支援快取,但是這樣我們的程式碼就有些臃腫了。

可以使用代理模式中的動態代理。舉個栗子:

如果是基於Spring框架來開發的話,那就可以在AOP切面中完成介面快取的功能。在應用啟動的時候,我們從配置文件中載入需要支援快取的介面,以及相應的快取策略(比如過期時間)等。當請求到來的時候,我們在AOP切面中攔截請求,如果請求中帶有支援快取的欄位(比如//…?..&cached=true),我們便從快取(記憶體快取或者Redis快取等)中獲取數據直接返回。

程式碼實現

這裡藉助於大話設計模式中的追女孩的栗子:

小明喜歡小紅,但是害羞不好意思出面,所以拜託好朋友,小張,出面給小紅送禮物。

小明是追求者,小張就是小明的中間人,也就是代理。

type GiveGift interface {
	GiveDolls() string
	GiveFlowers() string
	GiveChocolate() string
}

// 追求者
type Pursuit struct {
	GirlName string
}

func NewGirl(name string) *Pursuit {
	return &Pursuit{
		GirlName: name,
	}
}

func (ps *Pursuit) GiveDolls() string {
	return fmt.Sprintf("%s-送你娃娃", ps.GirlName)
}

func (ps *Pursuit) GiveFlowers() string {
	return fmt.Sprintf("%s-送你漂亮的鮮花", ps.GirlName)
}

func (ps *Pursuit) GiveChocolate() string {
	return fmt.Sprintf("%s-送你巧克力", ps.GirlName)
}

// 代理也就是中間人
type Proxy struct {
	Pursuit
}

func NewProxy(name string) *Pursuit {
	return NewGirl(name)
}

func (pr *Proxy) GiveDolls() string {
	return pr.GiveDolls()
}

func (pr *Proxy) GiveFlowers() string {
	return pr.GiveFlowers()
}

func (pr *Proxy) GiveChocolate() string {
	return pr.GiveChocolate()
}

最後放上一張結構圖

proxy

參考

【文中程式碼】//github.com/boilingfrog/design-pattern-learning/tree/master/代理模式
【大話設計模式】//book.douban.com/subject/2334288/
【極客時間】//time.geekbang.org/column/intro/100039001
【代理模式】//boilingfrog.github.io/2021/11/08/使用go實現代理模式/