背景
這兩種模式解決的問(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ì)者的目的.