概述
- 代理模式:代理模式在不改變原始類接口的條件下,爲原始類定義一個代理類,主要目的是控制訪問,而非加強功能,這是它跟裝飾器模式最大的不同。
- 裝飾器模式:裝飾者模式在不改變原始類接口的情況下,對原始類功能進行增強,並且支持多個裝飾器的嵌套使用。
- 適配器模式:適配器模式是一種事後的補救策略。適配器提供跟原始類不同的接口,而代理模式、裝飾器模式提供的都是跟原始類相同的接口。
代理模式
它在不改變原始類(或叫被代理類)代碼的情況下,通過引入代理類來給原始類附加功能。
- 一般情況下,讓代理類和原始類實現同樣的接口
- 如果原始類並沒有定義接口,並且原始類代碼並不是我們開發維護的,可以通過讓代理類繼承原始類的方法來實現代理模式
- 常用在業務系統中開發非功能性需求,比如:監控、統計、鑑權、限流、事務、冪等、日誌等
- 將附加功能與業務功能解耦,放到代理類統一處理,方便讓程序員只關注業務方面的開發
// author: [email protected]
public interface IRunner{
// 接口也可以替換成抽象類
void run();
}
public class Marathon : IRunner {
public void run() { //... }
}
public class CrossCountryProxy : IRunner {
private IRunner runner;
public CrossCountryProxy(IRunner runner) {
this.runner = runner;
}
public void run() {
// 新添加的代理邏輯
runner.run();
// 新添加的代理邏輯
}
}
裝飾器模式
裝飾器模式主要解決繼承關係過於複雜的問題,通過組合來替代繼承。它主要的作用是給原始類添加增強功能,這也是判斷是否該用裝飾器模式的一個重要的依據。
從代碼看,兩種模式完全一樣,只是其設計的側重點不同,簡單來講:
- 代理:偏重因自己無法完成或自己無需關心,需要他人干涉事件流程。
- 裝飾:偏重對原對象功能的擴展,擴展後的對象仍是是對象本身。
// author: [email protected]
public interface IRunner{
// 接口也可以替換成抽象類
void run();
}
public class Marathon : IRunner {
public void run() { //... }
}
public class CrossCountryDecorator : IRunner {
private IRunner runner;
public CrossCountryDecorator(IRunner runner) {
this.runner = runner;
}
public void run() {
// 功能增強代碼
runner.run();
// 功能增強代碼
}
}
適配器模式
用來做適配的,它將不兼容的接口轉換爲可兼容的接口,常用有兩種實現方式:
- 類適配器:使用繼承關係來實現
- 對象適配器:使用組合關係來實現
判斷使用哪個的標準一般有兩個,一個是 Adaptee 接口的個數,另一個是 Adaptee 和 ITarget 的契合程度
- 如果 Adaptee 接口並不多,那兩種實現方式都可以。
- 如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定義大部分都相同,那我們推薦使用類適配器,因爲 Adaptor 複用父類 Adaptee 的接口,比起對象適配器的實現方式,Adaptor 的代碼量要少一些。
- 如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定義大部分都不相同,那我們推薦使用對象適配器,因爲組合結構相對於繼承更加靈活。
使用適配器模式的場景一般有以下5種情況:
- 封裝有缺陷的接口設計
- 統一多個類的接口設計
- 替換依賴的外部系統
- 兼容老版本接口
- 適配不同格式的數據
// author: [email protected]
public interface IRunner {
void walk();
void run();
}
public class CrossCountry {
public void run(){}
public void eat(){}
}
// 類適配器: 基於繼承
public class SportsAdaptor : CrossCountry, IRunner {
public void walk() {
super.eat();
//其它行爲
}
// 跟對象適配器最大的不同點:
// 這裏run可以不實現,直接繼承自CrossCountry
}
// 對象適配器:基於組合
public class SportsAdaptor : IRunner {
private CrossCountry crossCountry;
public Adaptor(CrossCountry crossCountry) {
this.crossCountry = crossCountry;
}
public void walk() {
this.crossCountry.eat(); //委託給Adaptee
//其它行爲
}
//這裏run需要顯示實現
public void run() {
this.crossCountry.run();
}
}