分享
 
 
 

Delphi模式编程之策略模式(下)

王朝delphi·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

Delphi模式编程之策略模式(续)

刘 艺

1.3 策略模式在酒店管理系统中的应用

在酒店管理系统中,通常客房的价格不是一成不变的。对于住宿的淡季和旺季、老客户和新客户、散客和团队,都应该有不同的销售策略。显然,销售策略决定了报价。但是基于销售策略的报价体系又不能绑定于某一具体的客户端,因为只有把基于销售策略的报价体系独立出来,才能保证其重用性和可维护性。比如:一种报价体系一方面满足了优惠房价查询、客房结算等多个客户端的使用,另一方面又满足了不断调整的新销售策略的需求,这才算真正做到了重用性和可维护性。

对于以上的设计要求,选用策略模式是最好不过了。策略模式能够让算法变化独立于使用它的客户端。范例程序是一个基于策略模式的优惠房价查询模块,它包括一个基于销售策略的报价体系和一个优惠房价查询界面。当然,优惠房价查询界面只是该报价体系的客户端之一,报价体系亦可被其他客户端使用。

优惠房价查询模块的设计如图 1‑6所示。它包括了:

· 销售策略类TSaleStrategy,它是具体销售策略类的抽象基类。

· 3个具体销售策略类:TVipStrategy (VIP卡销售策略)、TTeamStrategy (团队销售策略)、TSeasonStrategy(季节销售策略)。

· 报价类TPRiceContext,它是该策略模式中的上下文,持有一个到TStrategy的引用。

· 客户端类TClient,它是一个窗体类,即房价查询的界面。

图1‑6 基于策略模式的优惠房价查询模块

示例程序 1‑1是HotelSaleStrategy单元的源代码,该单元包含了基于销售策略的报价体系的业务逻辑,用策略模式实现。TSaleStrategy作为销售策略的抽象基类,其目的是提供一个通用的接口。虚抽象函数SalePrice就是这样一个接口。由于3个具体销售策略分别是根据季节、VIP卡、团队人数来制定销售策略的,所以基类接口SalePrice的参数设计必须满足3个派生类的不同需求。TSaleStrategy的SalePrice函数声明如下:

function SalePrice(price:Currency;value:integer):Currency;

virtual; abstract;

它的第一个参数表示传入的固定房价,第二个参数表示传入的优惠条件,该条件因不同的派生类而异。在季节销售策略TSeasonStrategy中,该参数表示为入住月份;在VIP卡销售策略TVIPStrategy中,该参数表示为VIP卡的种类;在团队销售策略TTeamStrategy中,该参数表示为团队人数。我们发现,这些参数都可以用整数类型,所以在基类中,巧妙地用一个value参数解决了派生类的不同参数需求。这样一来,可以直接让TPriceContext将数据放在参数中传递给不同的销售策略类操作,避免了参数冗余。

{TPriceContext }

function TPriceContext.GetPrice(price:Currency;value:integer):Currency;

begin

result:=Strategy.SalePrice(price,value);

end;

TPriceContext在该策略模式中起着上下文作用,它负责引用销售策略对象的不同实例,调用SalePrice接口,动态配置具体的折扣算法,并返回实际销售价格。由于有了TPriceContext的中介,客户端无需知道具体销售策略是如何实现的;同样,当销售策略进行更新调整时,对客户端程序亦无影响。

示例程序1‑1 HotelSaleStrategy单元的源代码

unit HotelSaleStrategy;

interface

uses

SysUtils, Windows, Messages, Classes, Graphics, Controls,

Forms, Dialogs;

type

TSaleStrategy = class (TObject)

public

function SalePrice(price:Currency;value:integer):Currency;

virtual; abstract;

end;

TSeasonStrategy = class (TSaleStrategy)

public

function SalePrice(price:Currency;value:integer):Currency; override;

end;

TVIPStrategy = class (TSaleStrategy)

public

function SalePrice(price:Currency;value:integer):Currency; override;

end;

TTeamStrategy = class (TSaleStrategy)

public

function SalePrice(price:Currency;value:integer):Currency; override;

end;

TPriceContext = class (TObject)

private

FStrategy: TSaleStrategy;

procedure SetStrategy(Value: TSaleStrategy);

public

function GetPrice(price:Currency;value:integer):Currency;

property Strategy: TSaleStrategy read FStrategy write SetStrategy;

end;

implementation

{TSeasonStrategy }

function TSeasonStrategy.SalePrice(price:Currency;value:integer):Currency;

begin

//季节销售策略

{

2、3、11月8.5折优惠,

4、6月9折优惠。

8、9月9.5折优惠。

}

case value of

2,3,11:result:=price*0.85;

4,6:result:=price*0.9;

8,9:result:=price*0.95;

else

result:=price;

end;

end;

{TVIPStrategy }

function TVIPStrategy.SalePrice(price:Currency;value:integer):Currency;

begin

//VIP卡销售策略

{

0:VIP银卡 9折优惠

1:VIP金卡 8折优惠

2:VIP钻石卡 7 折优惠

}

case value of

0:result:=price*0.9;

1:result:=price*0.8;

2:result:=price*0.7;

end;

end;

{TTeamStrategy }

function TTeamStrategy.SalePrice(price:Currency;value:integer):Currency;

begin

//团队销售策略

{

3-5人团队9折优惠;

6-10人团队8折优惠;

11-20人团队7折优惠;

20人以上团队6折优惠。

}

result:=price;

if (value<6) and (value>2) then result:=price*0.9;

if (value<11) and (value>5) then result:=price*0.8;

if (value<21) and (value>10) then result:=price*0.7;

if (value>20) then result:=price*0.6;

end;

{TPriceContext }

function TPriceContext.GetPrice(price:Currency;value:integer):Currency;

begin

result:=Strategy.SalePrice(price,value);

end;

procedure TPriceContext.SetStrategy(Value: TSaleStrategy);

begin

FStrategy:=Value;

end;

end.

优惠房价查询模块的客户端程序如示例程序 1‑2所示。该程序提供一个用户选择界面,使得查询者可以任选一种优惠方案。一旦选定优惠条件和公开房价,点击“查询优惠房价”按钮,便得到打折后的优惠价。实际运行效果如图 1‑7所示。

示例程序1‑2 ClientForm单元的源代码

unit ClientForm;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ExtCtrls,HotelSaleStrategy, ComCtrls,DateUtils;

type

TClient = class(TForm)

RadioGroup1: TRadioGroup;

btnCheck: TButton;

btnExit: TButton;

dtpDate: TDateTimePicker;

cmbVIP: TComboBox;

Label1: TLabel;

Label2: TLabel;

cmbPrice: TComboBox;

edtPrice: TEdit;

Label3: TLabel;

edtCount: TEdit;

Label4: TLabel;

Label5: TLabel;

Bevel1: TBevel;

procedure FormCreate(Sender: TObject);

procedure btnCheckClick(Sender: TObject);

procedure FormDestroy(Sender: TObject);

procedure btnExitClick(Sender: TObject);

procedure RadioGroup1Click(Sender: TObject);

private

FSeasonStrategy:TSaleStrategy;

FVIPStrategy:TSaleStrategy;

FTeamStrategy:TSaleStrategy;

FPriceSys:TPriceContext;

public

{ Public declarations }

end;

var

Client: TClient;

implementation

{$R *.dfm}

procedure TClient.FormCreate(Sender: TObject);

begin

FSeasonStrategy:=TSeasonStrategy.Create;

FVIPStrategy:=TVIPStrategy.Create;

FTeamStrategy:=TTeamStrategy.Create;

FPriceSys:=TPriceContext.Create;

end;

procedure TClient.btnCheckClick(Sender: TObject);

var

i:integer;

price:Currency;

begin

case RadioGroup1.ItemIndex of

0:begin

FPriceSys.Strategy:=FSeasonStrategy ;

i:=MonthOf(dtpDate.DateTime);

end;

1:begin

FPriceSys.Strategy:=FVIPStrategy ;

i:=cmbVIP.ItemIndex;

end;

2:begin

FPriceSys.Strategy:=FTeamStrategy ;

i:=StrToInt(edtCount.Text);

end;

end;

case cmbPrice.ItemIndex of

0:price:=300 ; //甲类标准间300元

1:price:=500 ; //乙类标准间500元

2:price:=800 ; //贵宾间800元

3:price:=1000; //商务套房1000元

4:price:=2000; // 豪华套房2000元

end;

edtPrice.Text:=CurrToStr(FPriceSys.GetPrice(price,i));

end;

procedure TClient.FormDestroy(Sender: TObject);

begin

FPriceSys.Free;

FSeasonStrategy.Free;

FVIPStrategy.Free;

FTeamStrategy.Free;

end;

procedure TClient.btnExitClick(Sender: TObject);

begin

close;

end;

procedure TClient.RadioGroup1Click(Sender: TObject);

begin

dtpDate.Enabled:=false;

edtCount.Enabled:=false;

cmbVIP.Enabled:=false;

case RadioGroup1.ItemIndex of

0:dtpDate.Enabled:=true;

1:cmbVIP.Enabled:=true;

2:edtCount.Enabled:=true;

end;

end;

end.

图1‑7优惠房价查询模块的实际运行界面

1.4 实践小结

通过前面范例的演示和剖析,我们进一步讨论策略模式如下:

· 策略模式提供了管理算法集的办法。策略类的层次结构为TContext定义了一系列的可供重用的算法或行为。TStrategy基类析取出这些算法中的公共功能,派生类通过继承丰富了算法的差异和种类,又避免了重复的代码。

· 如果不将算法和使用算法的上下文分开,直接生成一个包含算法的TContext类的派生类,给它以不同的行为,这将会把行为写死到TContext中,而将算法的实现与TContext的实现混合起来,从而使TContext难以理解、难以维护和难以扩展。最后得到一大堆相关的类, 它们之间的唯一差别是它们所使用的算法。显然,类的继承关系是强关联,继承关系无法动态地改变算法;而对象的合成关系是弱关联,通过组合策略类对象,使得算法可以独立于使用算法的环境(TContext)而独立演化。

· 使用策略模式可以对大量使用条件分支语句的程序代码进行重构。当不同的行为堆砌在一个类中时,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的策略类中消除了这些条件语句。

· 过多的算法可能会导致策略对象的数目很大。为了减少系统开销,通常可以把依赖于算法环境的状态保存在客户端,而将TStrategy实现为可供各客户端共享的无状态的对象。任何外部的状态都由TContext维护。TContext在每一次对TStrategy对象的请求中都将这个状态传递过去。比如范例程序中,我将TSeasonStrategy的外部状态入住月份、TVIPStrategy的外部状态VIP卡的种类、TTeamStrategy的外部状态团队人数都保存在客户端,并通过TPriceContext将这些状态传递给销售策略类。这样做的好处是销售策略类变成无状态的了,它们同时可以被客房结算模块等其他模块共享。

· 无论各个具体策略实现的算法是简单还是复杂, 它们都共享TStrategy定义的接口。因此很可能某些具体策略不会都用到所有通过这个接口传递给它们的信息。如果我在范例程序中把TSaleStrategy的接口设计成这样:

SalePrice(price:Currency;Month:integer;VIP:integer;

Count:integer):Currency;

其中的一些参数永远不会被某些具体销售策略类用到。这就意味着有时TContext会创建和初始化一些永远不会用到的参数。如果存在这样问题,又无法使用范例程序中的技巧,那么只能在TStrategy和TContext之间采取紧耦合的方法。

更多相关文章和示例程序源代码可以到作者网站下载:http://www.liu-yi.net

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有