代理模式(Proxy) vs 裝飾器模式(Decorator)

背景

這兩種模式解決的問(wèn)題和場(chǎng)景其實(shí)是很不同的,但是看到過(guò)幾處地方總會(huì)拿出來(lái)比較。因?yàn)樗麄兊膶?shí)現(xiàn)代碼是非常相似的。

// 代理模式的代碼結(jié)構(gòu)
public interface IA { 
  void f();
}
public class A impelements IA { 
  public void f() {
   //... 
  }
}
public class AProxy impements IA { 
  private IA a; 
  public AProxy(IA a) { 
    this.a = a; 
  }
   public void f() { 
    // 新添加的代理邏輯 
    a.f(); 
    // 新添加的代理邏輯
  }
}

// 裝飾器模式的代碼結(jié)構(gòu)
public interface IA { 
  void f();
}
public class A impelements IA { 
  public void f() {
   //... 
  }
}
public class ADecorator impements IA { 
  private IA a; 
  public ADecorator(IA a) { 
    this.a = a; 
  }
   public void f() { 
    // 功能增強(qiáng)代碼
    a.f(); 
    // 功能增強(qiáng)代碼
  }
}

這兩個(gè)模式的簡(jiǎn)易實(shí)現(xiàn)代碼,除了名字之外是一模一樣的。(摘自極客時(shí)間)
但其實(shí)這樣的代碼示例我覺(jué)得是具有誤導(dǎo)性的。因?yàn)檠b飾器模式的話不可能只有一個(gè)ADecorator

what

代理模式:
在不改變?cè)碱?被代理類)代碼的情況下,通過(guò)引入代理類來(lái)給原始類附加功能

裝飾器模式:
裝飾器類是對(duì)功能的增強(qiáng),通過(guò)引入一個(gè)與原始類相同父類的子類,在實(shí)現(xiàn)方法中擴(kuò)展功能,我們可以對(duì)原始類嵌套多個(gè)裝飾器類

how

我們要給目前的播放器添加一個(gè)緩存功能,播放器本身只有單純的播放邏輯,為了不將緩存邏輯侵入到播放的業(yè)務(wù)邏輯里,我們將緩存功能的代碼放到代理類中完成。

簡(jiǎn)易代碼示例(Swift)

protocol Playable {
    func play()
}

class Player: Playable {
    func play() {
        // 播放器的播放邏輯
    }
}

class PlayerCacheDataProxy: Playable {
    let player: Playable
    init(player: Playable) {
      self.player = player
    }
    func play() {
        player.play()
        // 進(jìn)行緩存邏輯
    }
}

// 調(diào)用端
let player: Playable = PlayerCacheDataProxy()
player.play()

行為隔離和封裝是代理模式的主要體現(xiàn)。對(duì)于埋點(diǎn)統(tǒng)計(jì),為了不侵入到業(yè)務(wù)邏輯代碼中我們同樣可以使用這種思想。


玩過(guò)角色扮演游戲的玩家對(duì)我接下來(lái)的舉例應(yīng)該會(huì)理解很快。我們的角色是一個(gè)會(huì)射箭??的角色。我們用一個(gè)Shotable類定義射箭這個(gè)行為

protocol Shotable {
    func shot() 
}

角色的箭的種類有木箭,還有鐵箭,甚至還有手槍,畢竟他們都可以shot

// 木箭
class WoodenArrow: Shotable {
    func shot() {
        // 省略實(shí)現(xiàn)
    }
}
// 手槍
class Gun: Shotable {
    func shot() {
        // 省略實(shí)現(xiàn)
    }
}

除了基礎(chǔ)武器,還有附魔系統(tǒng),我們可以給武器附魔冰凍屬性,毒屬性,甚至還有連發(fā)增強(qiáng),所有的武器都可以具有以上的屬性增強(qiáng)。我們不可能為所有情況都定義不同的類,因?yàn)樗鼈兘M合的可能性太多了。
這個(gè)時(shí)候?qū)⒏侥У膶傩杂米餮b飾器類試試。

// 冰凍箭
class IceShoot: Shotable {

    let shooter: Shotable

    init(shooter: Shotable) {
      self.shooter = shooter
    }
    func shot() {
        // 加入冰凍屬性
        shooter.shot()
    }
}
// 毒箭
class PoisonousShoot: Shotable {

    let shooter: Shotable

    init(shooter: Shotable) {
      self.shooter = shooter
    }
    func shot() {
        // 加入毒屬性
        shooter.shot()
    }
}

我們接著就可以隨意打造我們的武器,在加入冰毒屬性后附毒都是可以的。

let arrow = WoodenArrow()
let iceArrow = IceShoot(shooter: arrow)
let iceAndPoisonArrow = PoisonousShoot(shooter: iceArrow)
// 木箭->冰凍->有毒
iceAndPoisonArrow.shot()

why

代理和裝飾器模式都用到了面向接口而非實(shí)現(xiàn)編程,都用到了組合而非繼承的思想。通過(guò)上面的例子可以看到他們解決的問(wèn)題是不一樣的。

都體現(xiàn)了封裝特性,裝飾器模式很好的利用了多態(tài)。
都為了增強(qiáng)/擴(kuò)展功能,但是裝飾器模式需要調(diào)用者自己去組裝,了解自己要去添加哪些具體功能,但是代理模式有時(shí)不像我上面舉的例子代理類那樣具體,是緩存還是統(tǒng)計(jì)還是說(shuō)完全交由外部去做都是有可能的。
比如iOS里的UITableViewDelegate。 他與傳統(tǒng)GoF提到的代理模式有些不同,它的作用更像是單純的回調(diào),暴露回調(diào)供給代理類自由做任何事情。

具體使用哪種模式還要看場(chǎng)景和設(shè)計(jì)者的目的.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容