前言
為什么要將Decorator(裝飾)
扁瓢,Delegation(委托)
详恼,Proxy(代理)
這三個模式放在一起呢?
因為它們的代碼是如此地相似涤妒。如果不結合場景单雾,會很容易分不清楚赚哗。
所以請在本文中嘗試體會它們當中的細微差別她紫。
我們應該記住,設計模式并非無中生有屿储,其原型往往來源于生活贿讹。
在面對對象編程(OOP
)時,我們的代碼可以看作現(xiàn)實世界某些場景的縮影够掠,我們的對象則可以看作是場景中的一個個人民褂。
因此看似相似的代碼所還原出的應用場景并不一定相同,它也就被抽象成不同的模式。
意圖
Decorator(裝飾)
側重于為一個基礎對象動態(tài)地增強它的職責或能力赊堪。
這是面對排列組合所造成的子類數(shù)爆炸問題的一種靈活的解決方案面殖。Delegation(委托)
側重于對用戶提供統(tǒng)一的接口,卻可以切換多種底層實現(xiàn)哭廉。
當使用委托對象的某個方法時脊僚,它不并自己實現(xiàn),而是往后退遵绰,委托給其內部的被委托對象讓其代勞辽幌。Proxy(代理)
側重于控制訪問。
不改變被代理對象的職責或能力椿访。提供與被代理對象相同的接口乌企,但會添加一些特有的邏輯來控制對被代理對象的訪問。
生動的栗子
有一個王國要打仗了成玫!王國的武器庫里有很多武器加酵。
比如說,有劍有斧子梁剔!
public interface Weapon {
int makeDamage();
}
public class Sword implements Weapon {
private int att = 10;
public int makeDamage() {
return this.att;
}
}
public class Axe implements Weapon {
private int att = 15;
public int makeDamage() {
return this.att;
}
}
斧子的初始攻擊比劍稍微高點虽画。但是沒關系,我們可以給它們加很多其他屬性荣病。比如說码撰,可以給武器加點毒屬性,也可以了來點鋼屬性个盆〔钡海總有一款適合你。
public abstract class WeaponDecorator implements Weapon {
protected Weapon weaponDecorated;
public WeaponDecorator(Weapon weaponDecorated) {
this.weaponDecorated = weaponDecorated;
}
public abstract int makeDamage();
}
public class PoisonWeapon extends WeaponDecorator{
private int powerOfPoison = 10;
public PoisonWeapon(Weapon weaponDecorated) {
super(weaponDecorated);
}
@Override
public int makeDamage() {
return weaponDecorated.makeDamage() + powerOfPoison;
}
}
public class SteelWeapon extends WeaponDecorator{
private int powerOfSteel = 12;
public SteelWeapon(Weapon weaponDecorated) {
super(weaponDecorated);
}
@Override
public int makeDamage() {
return weaponDecorated.makeDamage() + powerOfSteel;
}
}
我們就可以拿這些屬性來裝飾
實際的武器颊亮。
理論上我們一共可能得到這么多種武器柴梆。
Weapon normalSword = new Sword(); // 普通劍
Weapon steelSword = new SteelWeapon(new Sword()); //鋼劍
Weapon poisonSword = new PoisonWeapon(new Sword()); //毒劍
Weapon poisonSteelSword = new PoisonWeapon(new SteelWeapon(new Sword())); //毒鋼劍
Weapon normalAxe = new Axe(); // 普通斧
Weapon steelAxe = new SteelWeapon(new Axe()); //鋼斧
Weapon poisonAxe = new PoisonWeapon(new Axe()); //毒斧
Weapon poisonSteelAxe = new PoisonWeapon(new SteelWeapon(new Axe())); //毒鋼斧
我們忽略了屬性之間的順序,但有些時候順序是有意義的终惑,那么種類就會更多绍在。
這就是Decorator(裝飾)模式
了。
用2
個基類加上2
個裝飾器雹有,我們得到了8
種結果偿渡。如果又多了1
個基類,又多了2
個裝飾器呢霸奕?在不考慮順序的情況下溜宽,有48
種結果。
排列組合的威力太過強大质帅,如果不用裝飾模式适揉,我們得寫48
個子類出來留攒。
但有了裝飾器,我們只需要7
個類而已嫉嘀。而且可以非常靈活得組裝炼邀。
接著說故事~
我們的主角獸人大兄弟登場了。
他想要為王國效力剪侮。出戰(zhàn)前他可以去武器庫選把武器汤善。
public interface Warrior {
void attack();
int damageValue();
}
public class OrcWarrior implements Warrior {
private WeaponArsenal weaponArsenal = new WeaponArsenal();
public void attack() {
System.out.println(String.format("洛克打猴哥!造成%d點傷害!", damageValue()));
}
public int damageValue() {
return weaponArsenal.makeDamage();
}
public void selectWeapon(WeaponType weaponType) {
weaponArsenal.setWeaponType(weaponType);
}
}
public enum WeaponType {
SWORD, AXE
}
public class WeaponArsenal implements Weapon {
private WeaponType weaponType;
public int makeDamage() {
Weapon weapon = getWeapon();
if (weapon != null) {
return weapon.makeDamage();
}
return 0;
}
public void setWeaponType(WeaponType weaponType) {
this.weaponType = weaponType;
}
private Weapon getWeapon() {
switch (weaponType) {
case SWORD : return new SteelWeapon(new PoisonWeapon(new Sword()));
case AXE : return new SteelWeapon(new Axe());
default : return null;
}
}
}
這里就用到了Delegation(委托)模式
票彪。
當從武器庫選擇了武器的獸人戰(zhàn)士想要造成傷害時红淡,他委托了武器庫去造成傷害——weaponArsenal.makeDamage()
。
但武器庫怎么可能去造成傷害呢降铸?武器庫其實又是委托了具體被選擇的那把武器來造成傷害——weapon.makeDamage()
在旱。
獸人大兄弟十分英勇,但還是有時候贏有時候輸推掸。直到有一天他遇到了一個小法師桶蝎。
小法師完全不會打架,但智商很高谅畅。他發(fā)現(xiàn)我們這位獸人大兄弟看到什么都忍不住想上去砍一刀登渣,于是提議,由他來判斷形勢毡泻,可以打的時候就讓獸人兄弟上胜茧,打不了咱們就跑。
獸人兄弟沒多想仇味,一口答應呻顽。于是乎,小法師成為了獸人戰(zhàn)士的代理(Proxy)
丹墨。
public class LittleWizard implements Warrior {
private Warrior warrior;
public LittleWizard(Warrior warrior) {
this.warrior = warrior;
}
public void attack() {
if (warrior.damageValue() < 30) {
System.out.println("就這點攻擊力咱還是跑吧廊遍。");
} else {
System.out.println("不要慫,就是干贩挣!");
warrior.attack();
}
}
public int damageValue() {
return 0; // Little wizard doesn't know how to fight
}
}
有了小法師以后喉前,獸人的輸出被控制了,有了選擇性王财。
具體能不能打小法師說了算卵迂。
整個故事在戰(zhàn)場上是這樣的。
public class BattleField {
public static void main(String[] args) {
// 獸人大兄弟踏上了戰(zhàn)場
Warrior orcWarrior = new OrcWarrior();
// 選了把趁手的斧子
((OrcWarrior) orcWarrior).selectWeapon(WeaponType.AXE);
// 遇到誰都上去砍
orcWarrior.attack();
// 遇到了小法師搪搏,小法師成了獸人戰(zhàn)士的代理人
Warrior littleWizard = new LittleWizard(orcWarrior);
// 遇到敵人了狭握,慫了
littleWizard.attack();
// 趕緊回去換把武器
((OrcWarrior) orcWarrior).selectWeapon(WeaponType.SWORD);
// 上闪金!
littleWizard.attack();
}
}
結果是這樣的疯溺。
洛克打猴哥论颅!造成27點傷害!
就這點攻擊力咱還是跑吧。
不要慫囱嫩,就是干恃疯!
洛克打猴哥!造成32點傷害!
結論
上面雖然是一個魔幻現(xiàn)實主義場景墨闲,但我們會發(fā)現(xiàn)今妄,沒有一個模式是我們在生活中無法還原出來的,只是我們平時沒有認出它們鸳碧。
所以說設計模式源于生活盾鳞,而反過來將其還原成現(xiàn)實場景,就可以幫助我們更好地理解瞻离。
現(xiàn)實應用
-
Decorator(裝飾)模式
- 女生們每天早上不同的衣服搭配
- Java中的
FileInputStream()
那一系列類
-
Delegation(委托)模式
- 實在太過常見腾仅,因為我們經常把自己的某些工作委托給在那方面更擅長的人去做。比如說外包業(yè)務套利。
-
Proxy(代理)模式
- 限制訪問型——比如經紀人和明星推励。錢不到位我們是不能上的!
-
幫助訪問型——比如
VPN
肉迫,我們雖然訪問不了嗶~嗶验辞,但是我們可以訪問VPN
,而VPN
又可以訪問嗶~嗶喊衫。 - 各司其職型——這種情況下的代理模式和委托模式很相似跌造。還是比如經紀人和明星。經紀人負責找演出族购,明星負責唱歌跳舞鼻听。