Design Pattern - Proxy Pattern (代理模式)

Posted by Ryan Tseng on 2019-09-21

Proxy Pattern (代理模式)

Proxy Pattern 是一種結構化的 Pattern,他能夠控制對原始物件的操作,你可以在這其中加入一些其他的動作。

在 .NET 裡面使用代理模式最顯著的例子就屬 EntityFramework,他可以讓 EF 替原本什麼特殊功能都沒有的 POCO 加上變更偵測的功能,原因就是他使用代理的方式,在原有的類別上再加上了一些功能。

情境

當你有一個第三方的套件需要存取廠商所提供出來的 SDK,但是你發現這個廠商提供的 SDK 有一些問題(或許是會 Timeout),但是廠商並沒有針對這個問題去做處理,所以我想加入一些動作,當呼叫這個廠商提供的 SDK 中間,先在呼叫 SDK 中間先做一些檢查的動作,如果廠商 SDK 有問題的話,就乾脆不要發起呼叫

解決方案

在這樣的情境之下其實非常適合使用 Proxy 來幫我們處理,一開始文章開頭所提到的 EntityFramework 也是透過這樣的方式幫平庸無奇的 POCO 加上變更偵測所需要用到的一些屬性或是方法

所以針對這個情境,我們可以實作一個 Proxy 的類別,實作原本廠商提供的 Service,然後再幫他加點料上去,往後在呼叫 Service 時,可以呼叫我們自己做出來的 Proxy 類別,避免廠商的 SDK 產生的這些奇奇怪怪的問題。

實作重點

看到下面的 UML 圖,你可以知道 Proxy 和原本的 Service 都是實作同一個 介面,這意味著 Proxy 和 Service 會有相同的行為,不過你卻可以在 Proxy 中加入一些動作(或是稱為行為)

圖片出處 Refactoring Guru - Proxy Pattern

Show me the CODE!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 介面
export interface IFinancialLib {
getFinancialData(userId: string): void;
}

// 第三方套件
export class ThirdPartyFinancialLib implements IFinancialLib {
getFinancialData(userId: string): void {
console.log('Get financial data without handling any exception');
}
}

// 自訂套件
export class MyFinancialLib implements IFinancialLib {
constructor(private svc: ThirdPartyFinancialLib) {}
getFinancialData(userId: string): void {
// 這邊也可以加入原本沒有的 exception handling
try {
// 這邊可以幫原本的 3-rd Party 套件加上一些額外的檢查
if (this.isActive(userId)) {
console.log('get financial data when user is active');
const result = this.svc.getFinancialData(userId);
return result;
}
} catch (error) {
console.log('handle exceptions');
}
}

isActive(userId: string): boolean {
return true;
}
}

這邊可以看到執行的結果

結論

看完前面的程式碼範例,是不是覺得其實 Proxy 模式沒有這麼難理解了呢,同樣的也是要讓各位讀者知道,在學 Pattern 的時候,最好可以先知道這個 Pattern 想解決的問題,以及解決問題的來龍去脈,要用的時候才不會不小心選到一個不太適合當下情境的 Pattern。

今天的文章就到這邊,如果有什麼問題或是建議都可以留言給我,你們的建議都是小弟變得更強的養分!

參考資料