02-設計模式教學課件_第1頁
02-設計模式教學課件_第2頁
02-設計模式教學課件_第3頁
02-設計模式教學課件_第4頁
02-設計模式教學課件_第5頁
已閱讀5頁,還剩51頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第2章策略模式

(StrategyPattern)1.商場收銀軟件做一個商場收銀軟件,營業員根據客戶購買商品單價和數量,向客戶收費。關鍵代碼

double

total

=

0.0d;

private

void

btnOk_Click(object

sender,

EventArgs

e)

{

double

totalPrices=Convert.ToDouble(txtPrice.Text)

*

Convert.ToDouble(txtNum.Text);

total

=

total

+

totalPrices;

lbxList.Items.Add(“單價:”+txtPrice.Text+“

數量:”+txtNum.Text+“

合計:”+totalPrices.ToString());

lblResult.Text

=

total.ToString();

}無法滿足打折,滿300送300等促銷措施!!!報價管理在一些CRM(客戶關系管理)系統中,會有一個單獨的報價管理模塊,處理復雜的報價功能普通客戶:全價老客戶:根據年限,給予折扣大客戶:根據累計消費,給予折扣客戶購買量:對于新老客戶都適用報價人員職位高低:如何實現????3.簡單工廠實現類的劃分原則面向對象的編程,并不是類越多越好,類的劃分是為了封裝,但分類的基礎是抽象,具有相同屬性和功能的對象的抽象集合才是類。//現金收取父類

abstract

class

CashSuper

{

//抽象方法:收取現金,參數為原價,返回為當前價

public

abstract

double

acceptCash(double

money);

}

//正常收費,繼承CashSuper

class

CashNormal

:

CashSuper

{

public

override

double

acceptCash(double

money)

{

return

money;

}

}

//打折收費,繼承CashSuper

class

CashRebate

:

CashSuper

{

private

double

moneyRebate

=

1;

//初始化時,必需要輸入折扣率,如八折,就是0.8

public

CashRebate(string

moneyRebate)

{

this.moneyRebate

=

double.Parse(moneyRebate);

}

public

override

double

acceptCash(double

money)

{

return

money

*

moneyRebate;

}

}

//返利收費,繼承CashSuper

class

CashReturn

:

CashSuper

{

private

double

moneyCondition

=

0.0d;

private

double

moneyReturn

=

0.0d;

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

=

money-Math.Floor(money

/

moneyCondition)

*

moneyReturn;

return

result;

}

}

//現金收取工廠

class

CashFactory

{

//根據條件返回相應的對象

public

static

CashSuper

createCashAccept(string

type)

{

CashSuper

cs

=

null;

switch

(type)

{

case

"正常收費":

cs

=

new

CashNormal();

break;

case

"滿300返100":

CashReturn

cr1

=

new

CashReturn("300",

"100");

cs

=

cr1;

break;

case

"打8折":

CashRebate

cr2

=

new

CashRebate("0.8");

cs

=

cr2;

break;

}

return

cs;

}

}

//客戶端窗體程序(主要部分)

CashSuper

csuper;//聲明一個父類對象

double

total

=

0.0d;

private

void

btnOk_Click(object

sender,

EventArgs

e)

{

csuper

=

CashFactory.createCashAccept(cbxType.SelectedItem.ToString());

double

totalPrices=0;

totalPrices

=

csuper.acceptCash(Convert.ToDouble(txtPrice.Text)

*

Convert.ToDouble(txtNum.Text));

total

=

total

+

totalPrices;

lbxList.Items.Add("單價:"

+

txtPrice.Text

+

"

數量:"

+

txtNum.Text

+

"

"+cbxType.SelectedItem+

"

合計:"

+

totalPrices.ToString());

lblResult.Text

=

total.ToString();

}簡單工廠模式的缺陷折扣可能經常會變,公司周年慶的時候還會有額外折扣對于軟件開發者,不能不讓客戶提需求,需求的變更是必然!所以開發者應該考慮如何讓自己的程序更能適應變化,而不是抱怨客戶的無理。客戶不會管程序員加班時的汗水,也不相信程序員失業時的眼淚。假設:

增加積分功能等新功能簡單工廠模式雖然也能解決這個問題,但的確不是最好的辦法。另外由于商場是可能經常性的更改打折額度和返利額度,每次更改都需要改寫代碼重新編譯部署,面對算法的時常變動,應該有更好的辦法。4.策略模式(與簡單工廠類似)策略模式的用意是針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶端的情況下發生變化。使用策略模式可以把行為和環境分割開來。環境類負責維持和查詢行為類,各種算法則在具體策略類(ConcreteStrategy)中提供。由于算法和環境獨立開來,算法的增減、修改都不會影響環境和客戶端。當出現新的促銷折扣或現有的折扣政策出現變化時,只需要實現新的策略類,并在客戶端登記即可。策略模式相當于"可插入式(Pluggable)的算法"。

策略模式是對算法的包裝,是把使用算法的責任和算法本身分割開,委派給不同的對象管理。策略模式通常把一個系列的算法包裝到一系列的策略類里面,作為一個抽象策略類的子類。用一句話來說,就是:"準備一組算法,并將每一個算法封裝起來,使得它們可以互換。"

這個模式涉及到三個角色:環境(Context)角色:持有一個Strategy類的引用(上下文對象),負責和具體的策略類交互。抽象策略(Strategy)角色:這是一個抽象角色,通常由一個接口或抽象類實現。此角色給出所有的具體策略類所需的接口。具體策略(ConcreteStrategy)角色:包裝了相關的算法或行為。用策略模式修改上例定義出算法的接口把各種報價的計算方法獨立出來,形成算法類對于Context角色,把他當做上下文,在計算報價的時候,不再需要判斷,直接使用持有的具體算法進行運算即可。具體選擇使用哪一個算法功能放到客戶端CashSuper就是抽象策略正常收費CashNormal、打折收費CashRebate和返利收費CashReturn就是三個具體策略,也就是策略模式中說的具體算法環境角色??客戶端??環境角色

class

CashContext

{

private

CashSuper

cs;

public

CashContext(CashSuper

csuper)

{

this.cs

=

csuper;

}

public

double

GetResult(double

money)

{

return

cs.acceptCash(money);

}

}doubletotal=0.0d;

privatevoidbtnOk_Click(objectsender,EventArgse){

CashContextcc=null;switch(cbxType.SelectedItem.ToString()){case"正常收費":cc=newCashContext(newCashNormal());break;case"滿300返100":cc=newCashContext(newCashReturn("300","100"));break;case"打8折":cc=newCashContext(newCashRebate("0.8"));break;}

doubletotalPrices=0d;totalPrices=cc.GetResult(Convert.ToDouble(txtPrice.Text)*

Convert.ToDouble(txtNum.Text));total=total+totalPrices;lbxList.Items.Add("單價:"+txtPrice.Text+"數量:"+txtNum.Text+""+

cbxType.SelectedItem+"合計:"+totalPrices.ToString());lblResult.Text=total.ToString();}客戶端主要代碼模式講解策略模式功能把具體算法從具體業務處理中獨立策略模式與if-else語句多個if-else出現考慮使用策略模式算法的平等性策略算法是形同行為的不同實現誰來選擇具體策略算法客戶端由上下文來選擇具體的策略算法什么情況下應當使用策略模式

出現有許多相關的類,僅僅是行為有差別的情況,可以使用策略模式來使用多個行為中的一個來配置一個類的方法,實現算法動態切換出現同一個算法,有很多不同的實現的情況,可以使用策略模式來把這些“不同的實現”實現成為一個算法的類層次需要封裝算法中,與算法相關的數據的情況,可以使用策略模式來避免暴露這些跟算法相關的數據結構出現抽象一個定義了很多行為的類,并且是通過多個if-else語句來選擇這些行為的情況,可以使用策略模式來代替這些條件語句優點:策略模式可以避免讓客戶端涉及到不必要接觸到的復雜的和只與算法有關的數據。避免使用難以維護的多重條件選擇語句更好的擴展策略模式的缺點上述策略模式,把分支判斷又放回到客戶端,要改變需求算法時,還是要去更改客戶端的程序。客戶端必須知道所有的策略類,并自行決定使用哪一個策略類。這就意味著客戶端必須理解這些算法的區別,以便適時選擇恰當的算法類。增加了對象的數目只適合扁平的算法結構策略模式本質分離算法,選擇實現。

6.策略與簡單工廠結合把分支判斷放到到環境角色(CashContext類)中classCashContext{CashSupercs=null;

publicCashContext(stringtype){switch(type){case"正常收費":CashNormalcs0=newCashNormal();cs=cs0;break;case"滿300返100":CashReturncr1=newCashReturn("300","100");cs=cr1;break;case"打8折":CashRebatecr2=newCashRebate("0.8");cs=cr2;break;}}publicdoubleGetResult(doublemoney){returncs.acceptCash(money);}}客戶端doubletotal=0.0d;privatevoidbtnOk_Click(objectsender,EventArgse){//利用簡單工廠模式根據下拉選擇框,生成相應的對象

CashContextcsuper=newCashContext(cbxType.SelectedItem.ToString());doubletotalPrices=0d;//通過多態,可以得到收取費用的結果

totalPrices=csuper.GetResult(Convert.ToDouble(txtPrice.Text)*Convert.ToDouble(txtNum.Text));total=total+totalPrices;lbxList.Items.Add("單價:"+txtPrice.Text+"數量:"+txtNum.Text+""+cbxType.SelectedItem+"合計:"+totalPrices.ToString());lblResult.Text=total.ToString();}與簡單工廠客戶端代碼對比簡單工廠模式的用法

CashSupercsuper

=

CashFactory.createCashAccept(cbxType.SelectedItem.ToString());

…=csuper.acceptCash(…)策略模式和簡單工廠結合的方法

CashContextcsuper=

newCashContext(cbxType.SelectedItem.ToString());

…=csuper.GetResult(…)簡單工廠模式需要讓客戶端認識兩個類,改進的策略模式只需要讓客戶端認識一個類。耦合更加降低經典案例容錯恢復機制應用程序開發中常見的功能程序運行的時候,正常情況下應該按某種方式來做,如果按照某種方式來做發生錯誤的話,系統并不會崩潰,而是繼續運行,能提供出錯后的備用方案。日志記錄的例子把日志記錄到數據庫和日志記錄到文件當做兩種記錄日志的策略日志策略接口publicinterfaceLogStrategy{ publicvoidlog(Stringmsg);}實現日志策略接口publicclassDbLogimplementsLogStrategy{ publicvoidlog(Stringmsg){

if(msg!=null&&msg.trim().length()>5){ inta=5/0; } System.out.println("現在把'"+msg+"'記錄到數據庫中"); }}publicclassFileLogimplementsLogStrategy{ publicvoidlog(Stringmsg){

System.out.println("現在把'"+msg+"'記錄到文件中"); }}環境角色publicclassLogContext{ publicvoidlog(Stringmsg) { LogStrategystrategy=newDbLog(); try{ strategy.log(msg); }catch(Exceptionerr){ strategy=newFileLog(); strategy.log(msg); } }}客戶端publicclassClient{ publicstaticvoidmain(String[]args){

LogContextlog=newLogContext(); log.log("記錄日志"); log.log("再次記錄日志"); }}舉例:《三國演義》中的故事諸葛亮的錦囊妙計三條妙計走喬國老的后門,求孫國太放人,請孫夫人退兵趙云按計行事環境角色:趙云由他來決定選擇策略抽象策略角色:(接口)錦囊妙計按計行事(抽象方法)具體策略角色:三條妙計(單獨使用的)思考:解決簡單工廠模式中提到的問題了嗎????關鍵:分支的switch依然去不掉解決方法:依賴注入、反射、XML反射反射,程序員的快樂。模式深入在策略模式中,通常是上下文使用具體的策略實現對象,反過來,策略實現對象也可以從上下文獲取所需要的數據,因此可以將上下文當參數傳遞給策略實現對象在這種情況下,上下文封裝著具體策略對象進行算法運算所需要的數據,具體策略對象通過回調上下文的方法來獲取這些數據。

工資支付的例子很多企業的工資支付方式是很靈活的,可支付方式是比較多的,比如:人民幣現金支付、美元現金支付、銀行轉賬到工資帳戶、銀行轉賬到工資卡;一些創業型的企業為了留住骨干員工,還可能有:工資轉股權等等方式隨著公司的發展,會不斷有新的工資支付方式出現,這就要求能方便的擴展;另外工資支付方式不是固定的,是由公司和員工協商確定的,也就是說可能不同的員工采用的是不同的支付方式,甚至同一個員工,不同時間采用的支付方式也可能會不同,這就要求能很方便的切換具體的支付方式。要實現這樣的功能,策略模式是一個很好的選擇。在實現這個功能的時候,不同的策略算法需要的數據是不一樣,比如:現金支付就不需要銀行帳號,而銀行轉賬就需要帳號。這就導致在設計策略接口中的方法時,不太好確定參數的個數就算現在把所有的參數都列上了,今后擴展呢?難道再來修改策略接口嗎?修改接口,就要修改所有已有的實現,解決方案之一,就是把上下文當做參數傳遞給策略對象先實現人民幣現金支付和美元現金支付這兩種支付方式;然后再來添加銀行轉賬到工資卡的支付方式定義工資支付的策略接口(Java版)publicinterfacePaymentStrategy{

/**

*公司給某人真正支付工資

*@paramctx支付工資的上下文,里面包含算法需要的數據

*/

publicvoidpay(PaymentContextctx);}/**

*人民幣現金支付

*/publicclassRMBCashimplementsPaymentStrategy{

publicvoidpay(PaymentContextctx){

System.out.println("現在給"+ctx.getUserName()+"人民幣現金支付"+ctx.getMoney()+"元");

}}/**

*美元現金支付

*/publicclassDollarCashimplementsPaymentStrategy{

publicvoidpay(PaymentContextctx){

System.out.println("現在給"+ctx.getUserName()+"美元現金支付"+ctx.getMoney()+"元");

}}支付上下文的實現publicclassPaymentContext{

//應被支付工資的人員姓名

privateStringuserName=null;

//應被支付的工資的金額

privatedoublemoney=0.0;//支付工資的方式策略的接口

privatePaymentStrategystrategy=null;

publicPaymentContext(StringuserName,doublemoney,PaymentStrategystrategy){

this.userName=userName;

this.money=money;

this.strategy=strategy;}

publicStringgetUserName(){returnuserName;}

publicdoublegetMoney(){returnmoney;}

publicvoidpayNow(){

//使用客戶希望的支付策略來支付工資

this.strategy.pay(this);

}}publicclassClient{

publicstaticvoidmain(String[]args){

//創建相應的支付策略

PaymentStrategystrategyRMB=newRMBCash();

PaymentStrategystrategyDollar=newDollarCash();

//準備小李的支付工資上下文

PaymentContextctx1=newPaymentContext("小李",5000,strategyRMB);

//向小李支付工資

ctx1.payNow();

//切換一個人,給petter支付工資

PaymentContextctx2=newPaymentContext("Petter",8000,strategyDollar);

ctx2.payNow();

}}要求能支付到銀行卡,該怎么擴展最簡單呢?增加一種支付到銀行卡的策略實現,然后通過繼承來擴展支付上下文,在里面添加新的支付方式需要的新的數據,比如銀行卡賬戶,然后在客戶端使用新的上下文和新的策略實現,這樣已有的實現都不需要改變通過策略的構造方法來傳入新算法需要的數據。這樣實現的話,就不需要擴展上下文了,直接添加新的策略算法實現擴展的支付上下文對象的實現publicclassPaymentContext2

extendsPaymentContext{

//銀行帳號

privateStringaccount=null;

publicPaymentContext2(StringuserName,doublemoney,Stringaccount,PaymentStrategystrategy){

super(userName,money,strategy);

this.account=account;

}

publicStringgetAccount(){

returnaccount;

}}新的策略算法pu

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論