設(shè)計模式中的策略模式與狀態(tài)模式
使用場景
狀態(tài)模式:當(dāng)對象的行為隨對象的狀態(tài)的改變而改變時,我們?yōu)榱私怦疃嘀嘏袛鄺l件,封裝行為的變化,可以定義一個抽象的狀態(tài)類,提供對象行為接口。具體與狀態(tài)相關(guān)的行為,由它的子類去實現(xiàn)。
策略模式:“策略”二字等同于算法,當(dāng)現(xiàn)實系統(tǒng)中需要對算法動態(tài)指定,并且可以互相替換,可以抽象出算法的調(diào)用接口,具體的算法實現(xiàn)由具體的策略角色去實現(xiàn),根據(jù)里氏替換原則,任何出現(xiàn)父類的地方都可以使用它的子類去替換,這樣符合我們的業(yè)務(wù)需求。
比較
雖然兩者都是抽象出父類規(guī)范調(diào)用接口,具體的行為由子類實現(xiàn),環(huán)境對象同時包含父類的引用,但是這兩者模式應(yīng)用的場景完全不同。例如:你去ATM機取款,如果你的賬戶處于凍結(jié)狀態(tài),就不能正常取款。這里你的銀行賬戶至少有兩個狀態(tài)就是凍結(jié)與非凍結(jié)。這樣的業(yè)務(wù)解決方案你應(yīng)該不會想到用策略模式去解決。又如商場打折,有很多打折的策略,兒童用品打7折,老人用品打5折,這個問題不會跟狀態(tài)模式扯上關(guān)系吧,這里商品你不能說有老人狀態(tài)和兒童狀態(tài)!
ATM取款案例分析
設(shè)計到的角色:取款人、賬戶、ATM機。
用例圖
這邊簡單起見,就分析取款用例。
基本事件路徑
(1) 取款人插入銀行卡,輸入密碼;
(2) 選擇取款操作,輸入取款金額;
(3) 等待出鈔,取款。
在這個用例中,如果取款人賬戶已凍結(jié),就發(fā)生一個可選事件路徑,取款用例就被終止。
關(guān)鍵的業(yè)務(wù)需求是用戶取款事實,所以領(lǐng)域建模可以從標識取款和取款人這兩個類開始。這邊考慮到取款人不僅有取款這個用例,我們將取款泛化成交易。
建模
用例實化
取款:基本事件路徑的實化。
細化領(lǐng)域模型
這邊引入了ATMSystem這個邊界對象作為控制器負責(zé)與用戶交互(與用戶交互的ATM操作界面),Trade是負責(zé)處理用戶交易的類。
取款人插入銀行卡輸入密碼,ATMSystem負責(zé)將驗證賬戶信息的消息傳遞給Trade交易類,Trade根據(jù)傳過來的賬戶密碼驗證用戶是否存在,然后將處理結(jié)束的消息傳遞給ATMSystem這個邊界對象反映給用戶。
分析模型中的一些類
取款設(shè)計——應(yīng)用狀態(tài)模式
賬戶的狀態(tài)分為凍結(jié)狀態(tài)和激活狀態(tài)。賬戶在激活的狀態(tài)下可以進行交易,而在凍結(jié)狀態(tài)下不能做任何交易。在賬戶驗證的時候,應(yīng)該返回當(dāng)前賬戶的狀態(tài),如果賬戶處于凍結(jié)狀態(tài),當(dāng)取款人進行取款操作的時候直接返回錯誤提示信息。狀態(tài)圖表示如下:
取款應(yīng)用狀態(tài)模式結(jié)構(gòu)圖:
在驗證賬戶階段,如果賬戶處于凍結(jié)狀態(tài),則直接返回DeadAccount謝絕一切交易,否則返回ActiveAccount處理后面的交易。
示意代碼:
這邊為了簡單實現(xiàn),省去了ATMSystem這個邊界對象。
用戶賬號信息抽象類
- /// <summary>
- /// 用戶賬號信息
- /// </summary>
- public abstract class Account
- {
- /// <summary>
- /// 賬號
- /// </summary>
- private string account;
- /// <summary>
- /// 密碼
- /// </summary>
- private string pwd;
- public abstract decimal getBalance(decimal d);
- }
凍結(jié)賬號類
- /// <summary>
- /// 凍結(jié)賬戶類
- /// </summary>
- public class DeadAccount:Account
- {
- public override decimal getBalance(decimal d)
- {
- return 0;
- }
- }
激活賬戶類
- /// <summary>
- /// 激活賬戶類
- /// </summary>
- public class ActiveAccount:Account
- {
- public override decimal getBalance(decimal d)
- {
- return d;
- }
- }
交易類
- /// <summary>
- /// 交易類 負責(zé)用戶交易處理的類
- /// </summary>
- public class Trade
- {
- /// <summary>
- /// 保存用戶賬號信息
- /// </summary>
- private Account account;
- public Account VolidateLogin(Account a)
- {
- //query the database to validate the user exists
- //For Example
- this.account = new DeadAccount();
- return this.account;
- }
- /// <summary>
- /// 商業(yè)邏輯 取款
- /// </summary>
- /// <param name="d"></param>
- /// <returns></returns>
- public decimal GetBalance(decimal d)
- {
- return this.account.getBalance(d);
- }
- }
用戶類
- /// <summary>
- /// 取款人
- /// </summary>
- public class User
- {
- /// <summary>
- /// 用戶賬號信息
- /// </summary>
- private Account account;
- /// <summary>
- /// 交易處理類
- /// </summary>
- private Trade trade;
- public User(Account a, Trade t)
- {
- this.account = a;
- tthis.trade = t;
- }
- /// <summary>
- /// 用戶登錄類
- /// </summary>
- public void Login()
- {
- trade.VolidateLogin(account);
- }
- /// <summary>
- /// 取款
- /// </summary>
- /// <param name="d"></param>
- /// <returns></returns>
- public decimal GetBalance(decimal d)
- {
- return trade.GetBalance(d);
- }
- }
客戶端代碼
- class Client
- {
- static void Main(string[] args)
- {
- //開始用戶取款,默認是激活賬戶
- ActiveAccount aa = new ActiveAccount();
- Trade t = new Trade();
- User u = new User(aa,t);
- //先登錄,后取款
- u.Login();
- Console.WriteLine(u.GetBalance(100));
- Console.ReadLine();
- }
- }
用戶必須先登錄(插入銀行卡,輸入密碼,點確定),才能選擇取款業(yè)務(wù)(選擇取款)。登錄之后,返回的賬號對象作為trade類成員,當(dāng)進行取款業(yè)務(wù)的時候直接引用該賬號成員獲得取款信息。如果該賬號屬于凍結(jié)賬號,則直接返回0。否則返回取款的值。
商品折扣案例
案例描述:某家超市國慶節(jié)為了促銷,某些類商品打折,比如運動鞋打8折、秋裝打9折等,張三去購物為了一雙運動鞋、一件秋裝、一瓶洗發(fā)水。。。,張三買完東西回家,心想今天自己總共“賺”了多少錢?
案例分析:商家對于商品打折可能有很多策略,這里使用策略模式,封裝商品打折策略,這樣以便以后擴展了打折策略,不用去修改原來的代碼,具有很好的靈活性。
模式涉及的角色:
抽象策略角色:通常由一個接口或者抽象實現(xiàn);
具體策略角色:包裝了相關(guān)的算法和行為;
環(huán)境角色:包含抽象策略角色的引用,最終供用戶使用。
商品折扣案例設(shè)計圖
顧客購物涉及到的角色有:購物車、商品、收銀臺、抽象策略角色和具體策略角色。
購物車:是擺放商品的容器,提供添加和刪除操作;
商品:商品實體,有類型,商品名、價格等屬性;
收銀臺:負責(zé)收錢,主要是計算顧客購買所有商品的價格和折扣總額;
抽象策略角色:提供折扣策略接口。
具體策略角色:實現(xiàn)具體折扣算法。
商品折扣示意代碼:
- /// <summary>
- /// 具體商品類
- /// </summary>
- public class goods
- {
- /// <summary>
- /// 商品類型
- /// </summary>
- public string Type
- {
- set;
- get;
- }
- /// <summary>
- /// 商品名稱
- /// </summary>
- public string Name
- {
- get;
- set;
- }
- /// <summary>
- /// 商品價格
- /// </summary>
- public decimal Price
- {
- get;
- set;
- }
- }
抽象策略角色
- /// <summary>
- /// 抽象策略接口
- /// </summary>
- public interface IDiscountStrategy
- {
- decimal GetDiscount(goods g);
- }
具體策略角色
- /// <summary>
- /// 秋裝打折策略
- /// </summary>
- public class AutumnDressDiscountStrategy:IDiscountStrategy
- {
- #region IDiscountStrategy Members
- public decimal GetDiscount(goods g)
- {
- return (decimal)0.9 * g.Price;
- }
- #endregion
- }
- /// <summary>
- /// 運動鞋打折策略
- /// </summary>
- public class SportShoesDiscountStrategy:IDiscountStrategy
- {
- #region IDiscountStrategy Members
- public decimal GetDiscount(goods g)
- {
- return g.Price * (decimal)0.8;
- }
- #endregion
- }
購物車
|
收銀臺角色
- /// <summary>
- /// 收銀臺
- /// </summary>
- public class CashierDesk
- {
- /// <summary>
- /// 購物車
- /// </summary>
- private ShoppingCar shoppingCar;
- /// <summary>
- /// 策略字典
- /// </summary>
- private Dictionary<string, IDiscountStrategy> strategies;
- public CashierDesk(ShoppingCar sc, Dictionary<string, IDiscountStrategy> s)
- {
- this.shoppingCar = sc;
- this.strategies = s;
- }
- /// <summary>
- /// 獲得所有商品的價格
- /// </summary>
- /// <returns></returns>
- public decimal GetTotalPrice()
- {
- return shoppingCar.GoodsList.Sum(p => p.Price);
- }
- /// <summary>
- /// 獲得所有商品的總的折扣
- /// </summary>
- /// <returns></returns>
- public decimal GetTotalDiscount()
- {
- decimal sum = 0;
- IDiscountStrategy idiscountStrategy;
- foreach (goods g in shoppingCar.GoodsList)
- {
- idiscountStrategy=strategies.SingleOrDefault(p => p.Key == g.Type).Value;
- if (idiscountStrategy != null)
- {
- sum += idiscountStrategy.GetDiscount(g);
- }
- }
- return sum;
- }
- }
客戶端代碼
- class Client
- {
- static void Main(string[] args)
- {
- ShoppingCar sc = new ShoppingCar();
- Dictionary<string, IDiscountStrategy> discountD = new Dictionary<string, IDiscountStrategy>();
- //向購物車中加入商品
- sc.AddGoods(new goods {Name="NIKE鞋 ",Price=100,Type="運動鞋" });
- sc.AddGoods(new goods { Name = "秋裝", Price = 200, Type = "秋裝" });
- sc.AddGoods(new goods { Name = "蘋果", Price = 300, Type = "水果" });
- //配置折扣策略
- discountD.Add("運動鞋", new SportShoesDiscountStrategy());
- discountD.Add("秋裝", new AutumnDressDiscountStrategy());
- CashierDesk cd = new CashierDesk(sc, discountD);
- //得到所有商品總價
- Console.WriteLine(cd.GetTotalPrice());
- //得到所有商品折扣價
- Console.WriteLine(cd.GetTotalDiscount());
- Console.ReadLine();
- }
- }
策略模式優(yōu)點與缺點:
優(yōu)點
封裝了算法不穩(wěn)定性,易于以后業(yè)務(wù)策略的擴展。
缺點
每種策略對應(yīng)于一個具體策略角色類,會增加系統(tǒng)需要維護的類的數(shù)量。
原文鏈接:http://www.cnblogs.com/ejiyuan/archive/2012/06/28/2567905.html
【編輯推薦】