關於 Proxy 本篇將討論以下幾個問題
1. 關於 Proxy
2. UML
3. 將 UML 轉為程式碼
4. 情境
測試環境:
OS:Windows 10
IDE:Visual Studio 2019
1. 關於 Proxy
Provide a surrogate or placeholder for another object to control access to it.
by Gang of Four
- 為一對象(原實作)提供代理或替代,以控制對其(原實作)的訪問
Proxy(代理)屬於結構型(Structural Patterns),當遇到對象建立較耗費資源時,可使用 Proxy 來延遲建立,當真正需要使用到這個物件時才會建立,而非先建立好等待使用。當遇到控管或隔離的需求時,可使用 Proxy 來管理是否能夠取得被控管對象。
優點:
- 符合 開閉原則(Open Closed Principle)
缺點:
- 並非所有呼叫端都透過 Proxy 呼叫,程式複雜度增加
2. UML
Class 間關聯:
- Client 關聯 Subject
- RealSubject & Proxy 繼承 Subject
- Proxy 關聯 RealSubject
Class:
- Client:呼叫端
- Subject:被代理之服務的抽象類別或介面
- RealSubject:被代理之服務
- Proxy:代理
3. 將 UML 轉為程式碼
被代理之服務介面
/// <summary>
/// 被代理之服務介面
/// </summary>
public interface ISubject
{
void Request();
}
被代理之服務
/// <summary>
/// 被代理之服務
/// </summary>
public class RealSubject : ISubject
{
public void Request()
{
Console.WriteLine("Called RealSubject.Request()");
}
}
代理,繼承被代理之服務介面
/// <summary>
/// 代理,繼承被代理之服務介面
/// </summary>
public class Proxy : ISubject
{
private RealSubject _realSubject;
public void Request()
{
// Use 'lazy initialization'
if (_realSubject == null)
{
_realSubject = new RealSubject();
}
Console.WriteLine("Called Proxy.Request()");
_realSubject.Request();
}
}
- 建立代理
proxy
- 透過代理呼叫被代理物件
static void Main(string[] args)
{
Default.Proxy proxy = new Default.Proxy();
proxy.Request();
Console.ReadLine();
}
執行結果
Called Proxy.Request()
Called RealSubject.Request()
4. 情境
我們接到了一個銷售報表 API 加上權限的需求
- 角色分為店長 & 店員
- 店長可以看到銷售報表,店員無法
定義角色
/// <summary>
/// 角色
/// </summary>
public enum Position
{
StoreManager,
Clerk
}
銷售報表介面
/// <summary>
/// 銷售報表介面
/// </summary>
public interface IRevenueReport
{
void GetReport();
}
銷售報表實作
/// <summary>
/// 銷售報表實作
/// </summary>
public class RevenueReport : IRevenueReport
{
public void GetReport()
{
Console.WriteLine("Return revenue report.");
}
}
代理,繼承銷售報表介面
/// <summary>
/// 代理,繼承銷售報表介面
/// </summary>
public class Proxy : IRevenueReport
{
private RevenueReport _revenueReport { get; set; }
private Position _position { get; }
public Proxy(Position position)
{
_position = position;
Console.WriteLine($"Position:{_position.ToString()}");
}
public void GetReport()
{
if (_position != Position.StoreManager)
{
Console.WriteLine($"{_position.ToString()} can't get revenue report.");
return;
}
// Use 'lazy initialization'
if (_revenueReport == null)
{
_revenueReport = new RevenueReport();
}
_revenueReport.GetReport();
}
}
- 建立代理,分別使用店長 & 店員角色
- 透過代理呼叫取得報表
static void Main(string[] args)
{
Situation.Proxy proxyStoreManager = new Situation.Proxy(Situation.Position.StoreManager);
proxyStoreManager.GetReport();
Console.WriteLine($"\n");
Situation.Proxy proxyClerk = new Situation.Proxy(Situation.Position.Clerk);
proxyClerk.GetReport();
Console.ReadLine();
}
執行結果
Position:StoreManager
Return revenue report.
Position:Clerk
Clerk can't get revenue report.
完整程式碼
GitHub:Structural_07_Proxy