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

提交评论