2012年10月12日 星期五

Design Pattern : Decorator 裝飾模式 (1)

Decorator

使用時機:
A.系統需要新增新功能,但不想用繼承造成系統的子類別過多。
B.類別需要「動態」附加新功能。

方法:
1.  定義一個Interface和實作它的Class
2.  定義一個Abstract Class : Decorator 實作 interface
3.  定義其他裝飾類別封裝實體化Decorator類別,並覆寫(override)原有的方法。
4.  使用裝飾類別~ 宣告並取代原有的物件。

範例:
假設一個遊戲中,主角可以拿槍射擊敵人。
所以我們定義一個槍的interface : IGun class : CGun ,這是遊戲中最普通的槍。
隨著遊戲升級,武器可以加裝狙擊鏡Scope或是裝填雷射彈藥Laser ammo
他們所對應的開火情況便不相同。


1.  定義一個Interface和實作它的Class
/// <summary>
/// Abstract Class : CGun
/// </summary>
public interface IGun
{
    // 槍名
    String GunName { get; set;  }
    // 開火
    void Fire();
}

/// <summary>
    /// CGun實作IGun
    /// </summary>
    public class CGun : IGun
    {
        String sGunName;

        //建構子
        public CGun(String _GunName)
        {
            sGunName = _GunName;
        }

        // 槍名
        public String GunName
        {
            get { return sGunName; }
            set { sGunName = value; }
        }

        // 開火
        public void Fire()
        {
            Console.WriteLine(
                String.Format("{0}開火! ", sGunName));
        }
    }

2.  定義一個Abstract Class : Decorator 實作 interface

/// <summary>
    /// 實作IGun Decorator
    /// </summary>
   public abstract class Decorator : IGun
    {
        public String sGunName;
        protected CGun cGun = null//重要:需在此宣告一個protected CGun物件

        //透過建構子傳遞被修飾者
        public Decorator( CGun _cGun)
        {
            this.cGun = _cGun;
        }

        // 槍名
        public String GunName
        {
            get { return sGunName; }
            set { sGunName = value; }
        }

        //委託給被修飾者執行
        public virtual void Fire()
        {
            if (cGun != null)
            {
                this.cGun.Fire();
            }
        }
    }


3.  定義其他裝飾類別封裝實體化Decorator類別,並覆寫(override)原有的方法。

(A)
加裝狙擊鏡的裝飾類別
/// <summary>
    /// 可以加裝狙擊鏡的 裝飾類別
    /// </summary>
    public class CScopeDecorator : Decorator  //重要:實體化Decorator
    {
        //建構子 : 繼承基底類別的建構子
        public CScopeDecorator(CGun _cGun)
            : base(_cGun)
        {
            //建構子沒有變化...
        }

        //重寫父類別的Fire方法
        public override void Fire()
        {
            Console.WriteLine(
                String.Format("用裝了狙擊鏡的{0}開火! ", cGun.GunName ));
        }
}

(B)
裝填雷射彈藥的裝飾類別
/// <summary>
    /// 可以加裝雷射彈藥的 裝飾類別
    /// </summary>
    public class CLaserDecorator : Decorator
    {
        //建構子 : 繼承基底類別的建構子,但是改掉原本的槍名
        public CLaserDecorator(CGun _cGun)
            : base(_cGun)
        {
            //建構子有變化...
            _cGun.GunName = "Super Laser Rifle";
        }

        //重寫父類別的Fire方法
        public override void Fire()
        {
            Console.WriteLine(
                String.Format("用裝了雷射彈藥的{0}開火! ", cGun.GunName ));

            OverHeat();
        }

        //新增方法:槍管過熱
        private void OverHeat()
        {
            Console.WriteLine(
                String.Format("警告! {0} 槍管過熱。 ", cGun.GunName));
        }
}


4.  使用裝飾類別~ 宣告並取代原有的物件。


//宣告一個 CGun物件
IGun myGun = new CGun("Desert Eagle");
myGun.Fire();

//CScopeDecorator 裝飾原本的 CGun物件
myGun = new CScopeDecorator((CGun)myGun);
myGun.Fire();

//CLaserDecorator 裝飾原本的 CGun物件
IGun myGun2 = new CLaserDecorator
(
  new CGun("???")
);
   myGun2.Fire();


顯示結果
:

Desert Eagle
開火!
用裝了狙擊鏡的Desert Eagle開火!
用裝了雷射彈藥的Super Laser Rifle開火!
警告! Super Laser Rifle 槍管過熱。



沒有留言:

張貼留言