意圖
動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。
場景
在設計網絡游戲的武器系統時,開始並沒有考慮到武器的強化和磨損。之後,策劃人員說希望給游戲增加強化系統和修理系統,那麼我們的武器類型就需要對外提供強化、磨損、修理等方法了。發生這種改動是我們最不願意看到的,按照設計原則,我們希望功能的擴展盡可能不要修改原來的程序。你可能會想到使用繼承來實現,但是策劃人員的需求是有的武器能磨損能修理,不能強化,有的武器能強化,但是不會磨損,有的武器既能強化還能磨損和修理。遇到這樣的情況,繼承的方案可能不適合了,一來繼承的層次可能會很多,二來子類的數量可能會很多。
由此,引入裝飾模式來解決這個問題。裝飾模式使得我們能靈活賦予類額外的職責,並且使得設計和繼承相比更合理。
示例代碼
using System;
using System.Collections.Generic;
using System.Text;
namespace DecoratorExample
{
class Program
{
static void Main(string[] args)
{
Weapon w = new Rifle();
w.ShowInfo();
Enhance enhancedWeapon = new Enhance(w);
enhancedWeapon.EnhanceAmmo();
enhancedWeapon.ShowInfo();
Wear wornWeapon = new Wear(w);
wornWeapon.WearByRate(0.8);
wornWeapon.ShowInfo();
}
}
abstract class Weapon
{
private double ammo;
public double Ammo
{
get { return ammo; }
set { ammo = value; }
}
private double attack;
public double Attack
{
get { return attack; }
set { attack = value; }
}
private double speed;
public double Speed
{
get { return speed; }
set { speed = value; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
public abstract void ShowInfo();
}
class Rifle : Weapon
{
public Rifle()
{
this.Ammo = 100;
this.Attack = 10;
this.Speed = 5;
this.Name = "Rifle";
}
public override void ShowInfo()
{
Console.WriteLine(string.Format("ammo\t{0}", Ammo));
Console.WriteLine(string.Format("attack\t{0}", Attack));
Console.WriteLine(string.Format("speed\t{0}", Speed));
Console.WriteLine(string.Format("name\t{0}", Name));
}
}
abstract class Decorator : Weapon
{
protected Weapon w;
public Decorator(Weapon w)
{
this.w = w;
}
public override void ShowInfo()
{
w.ShowInfo();
}
}
class Enhance : Decorator
{
public Enhance(Weapon w) : base(w) { }
public void EnhanceAmmo()
{
w.Ammo += 20;
Console.WriteLine(">>>>>>>>>>>>Enhanced");
}
}
class Wear : Decorator
{
public Wear(Weapon w) : base(w) { }
public void WearByRate(double rate)
{
w.Speed = w.Speed * rate;
w.Attack = w.Attack * rate;
Console.WriteLine(">>>>>>>>>>>>Worn");
}
}
}
代碼執行結果如下圖:
代碼說明
l Weapon是抽象構件角色。
l Rifle是具體構件角色,實現抽象構件的接口。
l Decorator是裝飾角色。裝飾角色有兩個特點,一是繼承了抽象構件的接口,二是有一個構件角色的實例。
l Enhance和Wear是具體裝飾角色,它們負責給構件附加責任。
l 客戶端在使用裝飾角色的時候並沒有針對抽象構件進行編程,因為我們確實需要使用具體裝飾角色提供的額外方法,這種類型的裝飾叫做半透明裝飾。
何時采用
l 從代碼角度來說,如果你覺得由於功能的交叉擴展不會導致非常多的子類或者非常多的繼承層次的話可以考慮裝飾模式。
l 從應用角度來說,如果你希望動態給類賦予或撤銷一些職責,並且可以任意排列組合這些職責的話可以使用裝飾模式。
實現要點
l 讓裝飾角色還繼承抽象構件角色也是裝飾模式最大的特點,目的就是給抽象構件增加職責,對外表現為裝飾後的構件。
l 讓裝飾角色擁有構件角色實例的目的就是讓構件能被多個裝飾對象來裝飾。
l 在具體應用中可以靈活一點,不一定要有抽象構件和裝飾角色。但是,裝飾對象繼承裝飾對象並且擁有它實例的兩大特點需要體現。
l 透明裝飾一般通過在基類方法前後進行擴充實現,半透明裝飾一般通過新的接口實現。
注意事項
l 裝飾模式和橋接模式的區別是,前者是針對功能的擴展,本質上還是一樣東西,而後者針對多維護變化。裝飾模式的思想在於擴展接口而橋接模式的思想是分離接口。
l 裝飾類可能會比較瑣碎,並且不利於復用,裝飾模式在增加了靈活性的同時也降低了封裝度,在實際應用中可以和其它模式配合。