Strategy mode of Delphi mode programming (continued)
Liu Yi
1.3 Application of strategy model in hotel management system
In hotel management systems, usually the price of a room is not static. There should be different sales strategies for the off-season and peak seasons of accommodation, old customers and new customers, individual guests and groups. Obviously, the sales strategy determines the offer. However, the quotation system based on sales strategy cannot be bound to a specific client, because only by making the quotation system based on sales strategy independent can its reusability and maintainability be guaranteed. For example: on the one hand, a quotation system meets the needs of multiple clients such as preferential room rate inquiry and room settlement; on the other hand, it meets the needs of constantly adjusting new sales strategies. This can truly achieve reusability and maintainability. . For the above design requirements, it is best to choose the strategy mode. The Strategy pattern enables algorithm changes independent of the client using it. The sample program is a preferential housing price query module based on the strategy model, which includes a quotation system based on sales strategy and a preferential housing price query interface. Of course, the preferential price inquiry interface is only one of the clients of the quotation system, and the quotation system can also be used by other clients. The design of the preferential price inquiry module is shown in Figure 1-6. It includes: · Sales strategy class TSaleStrategy, which is an abstract base class for specific sales strategy classes. · 3 specific sales strategy categories: TVipStrategy (VIP card sales strategy), TTeamStrategy (team sales strategy), TSeasonStrategy (seasonal sales strategy). · Quotation class TPRiceContext, which is the context in this strategy pattern and holds a reference to TStrategy. · The client class TClient is a form class, which is the interface for house price inquiry. Figure 1-6 Sample program 1-1 of the preferential room rate inquiry module based on the strategy pattern is the source code of the HotelSaleStrategy unit. This unit contains the business logic of the quotation system based on the sales strategy and is implemented using the strategy pattern. As the abstract base class of sales strategy, TSaleStrategy aims to provide a common interface. The virtual abstract function SalePrice is such an interface. Since the three specific sales strategies are formulated based on season, VIP card, and team size, the parameter design of the base class interface SalePrice must meet the different needs of the three derived classes. The SalePrice function of TSaleStrategy is declared as follows: function SalePrice(price:Currency;value:integer):Currency; virtual; abstract; Its first parameter represents the incoming fixed price, and the second parameter represents the incoming preferential conditions. The conditions vary for different derived classes. In the seasonal sales strategy TSeasonStrategy, this parameter is expressed as the check-in month; in the VIP card sales strategy TVIPStrategy, this parameter is expressed as the type of VIP card; in the team sales strategy TTeamStrategy, this parameter is expressed as the number of people in the team. We found that these parameters can all use integer types, so in the base class, a value parameter is cleverly used to solve the different parameter requirements of the derived class. In this way, TPriceContext can directly put data in parameters and pass them to different sales strategy operations, avoiding parameter redundancy. {TPriceContext }function TPriceContext.GetPrice(price:Currency;value:integer):Currency;begin result:=Strategy.SalePrice(price,value);end;TPriceContext plays a contextual role in this strategy mode and is responsible for referencing the sales strategy Different instances of the object call the SalePrice interface to dynamically configure the specific discount algorithm and return the actual sales price. Due to the intermediary of TPriceContext, the client does not need to know how the specific sales strategy is implemented; similarly, when the sales strategy is updated and adjusted, it will have no impact on the client program. Sample program 1‑1 Source code of HotelSaleStrategy unit HotelSaleStrategy;interfaceuses 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 //Seasonal sales strategy {15% off in February, March and November, 10% off in April and June. 8. 9.5% off discount in September. } 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 card sales strategy{ 0: 10% off for VIP Silver Card 1: 20% off for VIP Gold Card 2: 30% off for VIP Diamond Card} 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 //Team sales strategy {10% off for a team of 3-5 people; 20% off for a team of 6-10 people; 30% off for a team of 11-20 people; 20 40% off for groups of more than 1 person. } 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. The client program of the preferential room rate query module is shown in Sample Program 1-2. The program provides a user selection interface so that the inquirer can choose a preferential plan. Once you select the preferential conditions and public rates, click the "Check Preferential Rates" button to get the discounted rates. The actual operation effect is shown in Figure 1-7. Sample program 1‑2 ClientForm unit source code unit ClientForm; interfaceuses 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; //Class A standard room is 300 yuan 1:price:=500; //Class B standard room is 500 yuan 2:price:=800; //VIP room is 800 yuan 3:price:=1000; //Business suite is 1,000 yuan 4:price:=2000; // Deluxe suite is 2,000 yuanend; 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); 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. Figure 1-7 The actual running interface of the preferential price query module
1.4 Summary of practice
Through the demonstration and analysis of the previous examples, we further discuss the strategy pattern as follows: · The strategy pattern provides a way to manage a set of algorithms. The hierarchy of strategy classes defines a series of reusable algorithms or behaviors for TContext. The TStrategy base class extracts the common functions in these algorithms, and the derived classes enrich the differences and types of algorithms through inheritance, and avoid duplication of code. · If you do not separate the algorithm from the context in which the algorithm is used, and directly generate a derived class of the TContext class that contains the algorithm, and give it different behaviors, this will hard-code the behavior into TContext, and separate the implementation of the algorithm from TContext. Implementations are mixed up, making TContext difficult to understand, maintain, and extend. The end result is a bunch of related classes, the only difference between them is the algorithm they use. Obviously, the inheritance relationship of classes is a strong association, and the inheritance relationship cannot dynamically change the algorithm; while the composition relationship of objects is a weak association. By combining strategy class objects, the algorithm can evolve independently of the environment (TContext) in which the algorithm is used. · Use the strategy pattern to refactor program codes that use a large number of conditional branch statements. When different behaviors are stacked in a class, it is difficult to avoid using conditional statements to select the appropriate behavior. Encapsulating behavior in separate policy classes eliminates these conditional statements. · Too many algorithms may result in a large number of policy objects. In order to reduce system overhead, the state that depends on the algorithm environment can usually be saved on the client, and TStrategy is implemented as a stateless object that can be shared by various clients. Any external state is maintained by TContext. TContext passes this state on every request to the TStrategy object. For example, in the sample program, I save the TSeasonStrategy's external status check-in month, the TVIPStrategy's external status VIP card type, and the TTeamStrategy's external status team size on the client, and pass these statuses to the sales strategy class through TPriceContext. The advantage of this is that the sales strategy class becomes stateless, and they can be shared by other modules such as the room settlement module. · Regardless of whether the algorithm implemented by each specific strategy is simple or complex, they all share the interface defined by TStrategy. Therefore it is likely that some specific policies will not use all the information passed to them through this interface. If I design the interface of TSaleStrategy like this in the sample program:
SalePrice(price:Currency;Month:integer;VIP:integer;
Count:integer):Currency;
Some of these parameters will never be used by certain sales strategy classes. This means that sometimes TContext will create and initialize parameters that will never be used. If there is such a problem and you cannot use the techniques in the sample program, you can only adopt a tightly coupled method between TStrategy and TContext.
More related articles and sample program source codes can be downloaded from the author's website: http://www.liu-yi.net