2016年2月29日 星期一

裝飾模式 Decorator Pattern

PM: 小奇,這次轉蛋活動營收不錯ㄟ~
我:   是喔~恭喜
PM: 所以我想再弄一個活動
PM: 就是把轉蛋轉到的裝備可以再次轉蛋
我:   .........
PM: 就是可利用轉蛋把裝備加上隨機四種屬性
PM: 例如XX裝備去轉蛋,轉蛋之後隨機增加裝備屬性
PM: 範例1: 力量 + 178  體 + 587 敏捷 + 20 智慧 + 50
PM: 範例2: 力量 + 58  體 + 1500 敏捷 + 35 智慧 + 98
PM: 轉一次50元,應該又可以在賺一波
我:   好的,交給我!
PM: 對了小奇,我可能還會加其他屬性,留點空間新增喔~

PM這次的需求一個某件裝備,可以加上N種屬性
再次翻一翻書,好像裝飾模式可以使用,來套套看好了!!

於是乎新增了一個
Armor.cs
using System;

namespace DesignPattern.DecoratorPattern
{
    /*裝備*/
    class Armor
    {
        public Armor()
        {}

        private string name;
        public Armor(string name)
        {
            this.name = name;
        }

        public virtual void Show()
        {
            Console.WriteLine("[" + name + "]");
        }
    }

    /*屬性*/
    class Attribute : Armor
    {
        protected Armor component;
        public void Decorate(Armor component)
        {
            this.component = component;
        }

        public override void Show()
        {
            if (component != null)
            {
                component.Show();
            }
        }
    }

    /*力量屬性*/
    class STR : Attribute
    {
        private int _val { get; set; }
        public STR(int val)
        {
            _val = val;
        }
        public override void Show()
        {
            Console.Write(" 力量 +" + _val);
            base.Show();
        }
    }

    /*體力屬性*/
    class CON : Attribute
    {
        private int _val { get; set; }
        public CON(int val)
        {
            _val = val;
        }
        public override void Show()
        {
            Console.Write(" 體力 +" + _val);
            base.Show();
        }
    }

    /*敏捷屬性*/
    class DEX : Attribute
    {
        private int _val { get; set; }
        public DEX(int val)
        {
            _val = val;
        }
        public override void Show()
        {
            Console.Write(" 敏捷 +" + _val);
            base.Show();
        }
    }

    /*智慧屬性*/
    class WIT : Attribute
    {
        private int _val { get; set; }
        public WIT(int val)
        {
            _val = val;
        }
        public override void Show()
        {
            Console.Write(" 智慧 +" + _val);
            base.Show();
        }
    }

    /*智力屬性*/
    class INT : Attribute
    {
        private int _val { get; set; }
        public INT(int val)
        {
            _val = val;
        }
        public override void Show()
        {
            Console.Write(" 智力 +" + _val);
            base.Show();
        }
    }

    /*精神屬性*/
    class MEN : Attribute
    {
        private int _val { get; set; }
        public MEN(int val)
        {
            _val = val;
        }
        public override void Show()
        {
            Console.Write(" 精神 +" + _val);
            base.Show();
        }
    }

}
新增主控台程式
Run.cs
using System;

namespace DesignPattern.DecoratorPattern
{
    class Run
    {
        static void Main()
        {
            Random rnd = new Random();
            
            Armor ar = new Armor("金屬盔甲");       /* 裝備*/

            /*PM第一種組合屬性*/
            STR str = new STR(rnd.Next(100));           
            str.Decorate(ar);  
            DEX dex = new DEX(rnd.Next(100));      
            dex.Decorate(str);
            CON con = new CON(rnd.Next(1000)); 
            con.Decorate(dex);
            WIT wit = new WIT(rnd.Next(500));       
            wit.Decorate(con);
            wit.Show();

            /*PM第二種組合屬性*/
            MEN men = new MEN(rnd.Next(100));
            men.Decorate(ar);
            INT _int = new INT(rnd.Next(100));
            _int.Decorate(men);
            _int.Show();

            Console.Read();
        }
    }
}

2016年2月26日 星期五

策略模式 Strategy Pattern

在轉蛋活動進行中的第2天,我還是一如往常的跟正妹聊MSN
突然PM又敲我MSN了... 

PM: 小奇,轉蛋活動賺翻拉,玩家都沒點數拉...
我: 是喔!
PM: 我想趁虛而入,阿~說錯是趁勝追擊
PM: 我想辦這個禮拜儲值點數到平台,多回饋20%點數
PM: 恩~算了!~還是買1000元送100點好了
PM: 哀阿~真的很難抉擇...我先想想好了你就先寫程式好了
我: 0.0...

我心中想了一會兒,這PM好像很善變,好像會有很多種折扣方式
看了一下書,好像可以使用策略模式來滿足PM的需求

於是乎我建立了一個
CashPurchasing.cs
using System;

namespace DesignPattern.StrategyPattern
{
    /*現金收取父類別*/
    abstract class CashPurchasing
    {
        public abstract double acceptCash(double money);
    }
}



再新增一個滿xxx元送xxx點的class繼承CashPurchasing
CashReturn.cs
using System;

namespace DesignPattern.StrategyPattern
{
    /*滿xxx元送xxx點*/
    class CashReturn : CashPurchasing
    {
        private double moneyCondition = 0.0d;
        private double moneyReturn = 0.0d;
        /*初始化時必須要輸入紅利條件和紅利值,比如滿300送100,則moneyCondition為300,moneyReturn為100*/
        public CashReturn(string moneyCondition, string moneyReturn)
        {
            this.moneyCondition = double.Parse(moneyCondition);
            this.moneyReturn = double.Parse(moneyReturn);
        }

        public override double acceptCash(double money)
        {
            double result = money;
            if (money >= moneyCondition)
                result = moneyReturn;

            return result;
        }
    }
}


在新增另一個計算回饋的class一樣繼承CashPurchasing
CashRebate.cs
using System;

namespace DesignPattern.StrategyPattern
{
    /*回饋點數,繼承CashSuper*/
    class CashRebate : CashPurchasing
    {
        private double moneyRebate = 1d;
        /*初始化時,必需要輸入回饋率,如20%,就是0.2*/
        public CashRebate(string moneyRebate)
        {
            this.moneyRebate = double.Parse(moneyRebate);
        }

        public override double acceptCash(double money)
        {
            return money * moneyRebate;
        }
    }
}

PM要的兩種類別都寫好了!!
若以後PM再想到有的沒得運算邏輯就在新增類別即可
寫到這邊,好像似曾相識怎麼跟簡單工廠的做法一樣
再來是不是又要像簡單工廠一樣使用一個SimpleFactory來呼叫需要使用的類別?
所以邊要利用策略模式加上簡單工廠模式

於是乎又建立一個策略模式的class
CashContext.cs
using System;

namespace DesignPattern.StrategyPattern
{
    class CashContext
    {
        CashPurchasing cp = null;

        //根據條件返回相應的物件
        public CashContext(string type)
        {
            switch (type)
            {
                /*滿1000送100*/
                case "return":
                    CashReturn cr1 = new CashReturn("1000", "100");
                    cp = cr1;
                    break;
                /*回饋20%*/
                case "rebate":
                    CashRebate cr2 = new CashRebate("0.2");
                    cp = cr2;
                    break;
            }
        }

        public double GetResult(double money)
        {
            return cp.acceptCash(money);
        }
    }
}

再利用主控台跑一下這一個策略模式
using System;

namespace DesignPattern.StrategyPattern
{
    class RunGame
    {
        static void Main()
        {
            try
            {                
                Console.Write("請輸入玩家儲值的點數:");
                string strAmount = Console.ReadLine();

                /*PM要做回饋20%*/
                CashContext cp = new CashContext("rebate");
                Console.WriteLine("PM要做回饋20%,恭喜您可以額外獲得:" + cp.GetResult(Convert.ToInt32(strAmount)));

                /*PM要做滿1000送100*/
                cp = new CashContext("return");
                Console.WriteLine("PM要做滿1000送100,恭喜您可以額外獲得:" + cp.GetResult(Convert.ToInt32(strAmount)));

                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("您輸入有錯:" + ex.Message);
                Console.ReadLine();
            }

        }
    }
}

所以策略模式中我只要認識CashContext類別就好了
但上一篇的簡單工廠模式我要認識Gashapon與SimpleFactory類別
所以使用策略模式就降低了耦合度


比較 簡單工場模式 與 策略模式: 

1. 簡單工廠模式解決的是物件建立的問題,策略模式的重點則在用一個方式調用一系列演算法,使演算法的變動不會影響到用戶端
2. 對用戶端來說,使用簡單工廠模式必須 import 父類別跟 Factory 類別;但使用策略模式時只需 import 一個 Context 類別就行,降低耦合度

2016年2月25日 星期四

簡單工廠模式 Simple Factory Pattern

有一天當我在逛著Facebook的時候,PM突然敲我MSN,那時候非常流行MSN真的不要懷疑
我當時引以自豪的收集非常多的MSN表情圖示,讓我被當神一樣的崇拜
好了廢話不多說,以下是我跟PM的對話...

PM: 小奇,可以幫我弄一個營收活動嗎?
我:   當然可以
PM: 我想做一個轉蛋的活動
我:   是喔
PM: 就做成小瑪莉那樣好了,總共有12格

就類似以下圖示12格每一個會放一個虛寶

我:   好的沒問題喔
PM: 但我想要有一個條件,就是轉一次50元
PM: 我想讓玩家轉了1000元以上才有機會獲得高級實體虛寶,可以嗎?
我:   這.....雖然黑心了一點,當然沒問題XD

於是乎我就建立了一個
Gashapon.cs
using System;

namespace DesignPattern.SimpleFactoryPattern
{
    /*建立一個轉蛋的類別*/
    public class Gashapon
    {
        /*玩家被騙的金額*/
        private double _Amount = 0;

        public double Amount
        {
            get { return _Amount; }
            set { _Amount = value; }
        }

        /*玩家獲得的虛寶*/
        public virtual string GetItem()
        {
            string result = "";
            return result;
        }
    }
}


接著再建立一個高級虛寶的class繼承Gashapon

   class AdvancedItem : Gashapon
    {
        /*高級實體商品*/
        public override string GetItem()
        {
            /*高級實體商品邏輯運算,這邊簡略不實作*/
            return "高級實體商品";
        }
    }

在建立一個只能獲得普通虛寶的class繼承Gashapon
這一個類別裡面就只會計算普通虛寶的編號
    class GeneralItem : Gashapon
    {
        /*一般虛寶*/
        public override string GetItem()
        {
            /*一般爛虛寶邏輯運算,這邊簡略不實作*/
            return "一般爛虛寶";
        }
    }

所以這三個類別的用意就是,當如果玩家被騙了超過1000元我就要呼叫高級虛寶的類別來計算獲得高級虛寶的編號
如果未滿1000元就呼叫普通虛寶的類別計算,這樣玩家就會不知不覺狂抽,來增加營收
所以此時就可以利用簡單工廠模式來達成這樣的需求

所以在建立一個簡單工廠的class
    class SimpleFactory
    {
        public static Gashapon getItem(int amount)
        {
            Gashapon gash = null;

            /*當玩家消費超過1000元*/
            if (amount >= 1000)
                gash = new AdvancedItem();  /*呼叫高級實體商品*/
            else
                gash = new GeneralItem();   /*呼叫一般爛虛寶*/

            return gash;
        }
    }

所以整個完整的類別如以下
using System;

namespace DesignPattern.SimpleFactoryPattern
{
    /*建立一個轉蛋的類別*/
    public class Gashapon
    {
        /*玩家被騙的金額*/
        private double _Amount = 0;

        public double Amount
        {
            get { return _Amount; }
            set { _Amount = value; }
        }

        /*玩家獲得的虛寶*/
        public virtual string GetItem()
        {
            string result = "";
            return result;
        }
    }

    class AdvancedItem : Gashapon
    {
        /*高級實體商品*/
        public override string GetItem()
        {
            /*高級實體商品邏輯運算,這邊簡略不實作*/
            return "高級實體商品";
        }
    }

    class GeneralItem : Gashapon
    {
        /*一般虛寶*/
        public override string GetItem()
        {
            /*一般爛虛寶邏輯運算,這邊簡略不實作*/
            return "一般爛虛寶";
        }
    }

    /*簡單工廠類別*/
    class SimpleFactory
    {
        public static Gashapon getItem(int amount)
        {
            Gashapon gash = null;

            /*當玩家消費超過1000元*/
            if (amount >= 1000)
                gash = new AdvancedItem();  /*呼叫高級實體商品*/
            else
                gash = new GeneralItem();   /*呼叫一般爛虛寶*/

            return gash;
        }
    }
}
再利用主控台跑一下這一個簡單工廠模式,沒有問題就開始痛宰玩家了~哈哈
using System;

namespace DesignPattern.SimpleFactoryPattern
{
    class RunGame
    {
        static void Main()
        {
            try
            {
                Console.WriteLine("轉蛋開始...");
                Console.Write("請輸入玩家已消費金額");
                string strAmount = Console.ReadLine();

                Gashapon gash = null;
                gash = SimpleFactory.getItem(Convert.ToInt32(strAmount));

                Console.WriteLine("恭喜您獲得:" + gash.GetItem());
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("您輸入有錯:" + ex.Message);
                Console.ReadLine();
            }

        }
    }
}