示例:影片出租店程序(重构——分解并重组Statement)
步骤:
1、提炼“金额计算”代码
1.1、提炼“逻辑泥团”——“提炼方法(Extract Method)”
Statement()中一个明显的“逻辑泥团”就是case语句,把它提炼到独立函数“AmountFor”中。
function TCustomer.Statement: string;
var
totalAmount: double; //--总消费金额
frequentRenterPoints: integer; //--常客积点
Rentals: TEnumeration;
thisAmount: double;
each: TRental;
//---计算一笔租片费用
function AmountFor(each: TRental): double;
var
thisAmount: double;
begin
thisAmount := 0;
case each.Movie.PriceCode of //--取得影片出租价格
REGULAR: //--普通片
begin
thisAmount := thisAmount + 2;
if each.DaysRented > 2 then
thisAmount := thisAmount + (each.DaysRented - 2) * 1.5;
end;
NEW_RELEASE: //--新片
begin
thisAmount := thisAmount + each.DaysRented * 3;
end;
CHILDRENS: //--儿童片
begin
thisAmount := thisAmount + 1.5;
if each.DaysRented > 3 then
thisAmount := thisAmount + (each.DaysRented - 3) * 1.5;
end;
end;
//---
Result := thisAmount;
end;
begin
Result := 'Rental Record for ' + self.Name + #13#10;
//---
totalAmount := 0;
frequentRenterPoints := 0;
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement); //--取得一笔租借记录
thisAmount := AmountFor(each);
//---累加常客积点
frequentRenterPoints := frequentRenterPoints + 1;
if (each.Movie.PriceCode = NEW_RELEASE) and (each.DaysRented > 1) then
frequentRenterPoints := frequentRenterPoints + 1;
//---显示此笔租借数据
Result := Result + each.Movie.Title + ' ' + FloatToStr(thisAmount) + #13#10;
totalAmount := totalAmount + thisAmount;
end;
Rentals.Free;
//---结尾打印
Result := Result + 'Amount owed is ' + FloatToStr(totalAmount) + #13#10;
Result := Result + 'You earned ' + IntToStr(frequentRenterPoints) + ' frequent renter points';
end;
1.2、规范变量名称
好的代码应该清楚表达出自己的功能,变量名称是代码清浙的关键。
//---计算一笔租片费用
function AmountFor(ARental: TRental): double;
begin
Result := 0;
case ARental.Movie.PriceCode of //--取得影片出租价格
REGULAR: //--普通片
begin
Result := Result + 2;
if ARental.DaysRented > 2 then
Result := Result + (ARental.DaysRented - 2) * 1.5;
end;
NEW_RELEASE: //--新片
begin
Result := Result + ARental.DaysRented * 3;
end;
CHILDRENS: //--儿童片
begin
Result := Result + 1.5;
if ARental.DaysRented > 3 then
Result := Result + (ARental.DaysRented - 3) * 1.5;
end;
end;
end;
1.3、搬移“金额计算”代码——“搬移方法(Move Method)”
AmountFor函数使用了来自Rental类的信息.却没有使用来自Customer类的信息。这表明它可能是被放错了位置,应
将AmountFor()移到Rental类中。此外,还要在搬移的同时变更函数名称(AmountFor –> GetCharge)。
function TRental.GetCharge: double;
{计算一笔租片费用}
begin
Result := 0;
//---
case self.Movie.PriceCode of //--取得影片出租价格
REGULAR: //--普通片
begin
Result := Result + 2;
if self.DaysRented > 2 then
Result := Result + (self.DaysRented - 2) * 1.5;
end;
NEW_RELEASE: //--新片
begin
Result := Result + self.DaysRented * 3;
end;
CHILDRENS: //--儿童片
begin
Result := Result + 1.5;
if self.DaysRented > 3 then
Result := Result + (self.DaysRented - 3) * 1.5;
end;
end;
end;
function TCustomer.Statement: string;
var
totalAmount: double; //--总消费金额
frequentRenterPoints: integer; //--常客积点
Rentals: TEnumeration;
thisAmount: double;
each: TRental;
begin
Result := 'Rental Record for ' + self.Name + #13#10;
//---
totalAmount := 0;
frequentRenterPoints := 0;
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
thisAmount := each.GetCharge; //--取得一笔租借记录
//---累加常客积点
frequentRenterPoints := frequentRenterPoints + 1;
if (each.Movie.PriceCode = NEW_RELEASE) and (each.DaysRented > 1) then
frequentRenterPoints := frequentRenterPoints + 1;
//---显示此笔租借数据
Result := Result + each.Movie.Title + ' ' + FloatToStr(thisAmount) + #13#10;
totalAmount := totalAmount + thisAmount;
end;
Rentals.Free;
//---结尾打印
Result := Result + 'Amount owed is ' + FloatToStr(totalAmount) + #13#10;
Result := Result + 'You earned ' + IntToStr(frequentRenterPoints) + ' frequent renter points';
end;
1.4、删除临时变量——“替换临时变量(Replace Temp with Query)”
Statement方法中的临时变量thisAmount接受each.GetCharge的执行结果,然后就不再有任何改变。所以尽量除去这
一类临时变量。
function TCustomer.Statement: string;
var
totalAmount: double; //--总消费金额
frequentRenterPoints: integer; //--常客积点
Rentals: TEnumeration;
each: TRental;
begin
Result := 'Rental Record for ' + self.Name + #13#10;
//---
totalAmount := 0;
frequentRenterPoints := 0;
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
//---累加常客积点
frequentRenterPoints := frequentRenterPoints + 1;
if (each.Movie.PriceCode = NEW_RELEASE) and (each.DaysRented > 1) then
frequentRenterPoints := frequentRenterPoints + 1;
//---显示此笔租借数据
Result := Result + each.Movie.Title + ' ' + FloatToStr(each.GetCharge) + #13#10;
totalAmount := totalAmount + each.GetCharge;
end;
Rentals.Free;
//---结尾打印
Result := Result + 'Amount owed is ' + FloatToStr(totalAmount) + #13#10;
Result := Result + 'You earned ' + IntToStr(frequentRenterPoints) + ' frequent renter points';
end;
2、提炼“常客积点计算”代码
2.1、提炼“常客积点计算”代码——“提炼函数(Extract Method)”
首先,我们需要针对“常客积点计算”这部分代码运用“提炼函数(Extract Method)”重构准则,将其提炼为函
数“GetFrequentRenterPoints”。
function TCustomer.Statement: string;
var
totalAmount: double; //--总消费金额
frequentRenterPoints: integer; //--常客积点
Rentals: TEnumeration;
each: TRental;
//---获取常客积点
function GetFrequentRenterPoints: integer;
begin
frequentRenterPoints := frequentRenterPoints + 1;
if (each.Movie.PriceCode = NEW_RELEASE) and (each.DaysRented > 1) then
frequentRenterPoints := frequentRenterPoints + 1;
end;
begin
Result := 'Rental Record for ' + self.Name + #13#10;
//---
totalAmount := 0;
frequentRenterPoints := 0;
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
//---累加常客积点
GetFrequentRenterPoints;
//---显示此笔租借数据
Result := Result + each.Movie.Title + ' ' + FloatToStr(each.GetCharge) + #13#10;
totalAmount := totalAmount + each.GetCharge;
end;
Rentals.Free;
//---结尾打印
Result := Result + 'Amount owed is ' + FloatToStr(totalAmount) + #13#10;
Result := Result + 'You earned ' + IntToStr(frequentRenterPoints) + ' frequent renter points';
end;
然后,在“GetFrequentRenterPoints”函数内找到局部变量each,它可以被当作参数传入新函数中。另一个临时变
量是frequentRenterPoints。本例中的它在被使用之前已经先有初值,但提炼出来的函数并没有读取该值,所以我
们不需要将它当作参数传进去,只需对它执行“追加赋值操作“就行 。
function TCustomer.Statement: string;
var
totalAmount: double; //--总消费金额
frequentRenterPoints: integer; //--常客积点
Rentals: TEnumeration;
each: TRental;
//---获取常客积点
function GetFrequentRenterPoints(each: TRental):integer;
var
frequentRenterPoints: integer; //--常客积点
begin
frequentRenterPoints := 1;
if (each.Movie.PriceCode = NEW_RELEASE) and (each.DaysRented > 1) then
frequentRenterPoints := frequentRenterPoints + 1;
//---
Result := frequentRenterPoints;
end;
begin
Result := 'Rental Record for ' + self.Name + #13#10;
//---
totalAmount := 0;
frequentRenterPoints := 0;
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
//---累加常客积点
frequentRenterPoints := frequentRenterPoints + GetFrequentRenterPoints(each);
//---显示此笔租借数据
Result := Result + each.Movie.Title + ' ' + FloatToStr(each.GetCharge) + #13#10;
totalAmount := totalAmount + each.GetCharge;
end;
Rentals.Free;
//---结尾打印
Result := Result + 'Amount owed is ' + FloatToStr(totalAmount) + #13#10;
Result := Result + 'You earned ' + IntToStr(frequentRenterPoints) + ' frequent renter points';
end;
2.2、规范变量名称
//---获取常客积点
function GetFrequentRenterPoints(each: TRental):integer;
begin
if (each.Movie.PriceCode = NEW_RELEASE) and (each.DaysRented > 1) then
Result := 2
else
Result := 1;
end;
2.3、搬移“常客积点计算”代码——“搬移方法(Move Method)”
function TRental.GetFrequentRenterPoints: integer;
{获取常客积点}
begin
if (self.Movie.PriceCode = NEW_RELEASE) and (self.DaysRented > 1) then
Result := 2
else
Result := 1;
end;
function TCustomer.Statement: string;
var
totalAmount: double; //--总消费金额
frequentRenterPoints: integer; //--常客积点
Rentals: TEnumeration;
each: TRental;
begin
Result := 'Rental Record for ' + self.Name + #13#10;
//---
totalAmount := 0;
frequentRenterPoints := 0;
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
//---累加常客积点
frequentRenterPoints := frequentRenterPoints + each.GetFrequentRenterPoints;
//---显示此笔租借数据
Result := Result + each.Movie.Title + ' ' + FloatToStr(each.GetCharge) + #13#10;
totalAmount := totalAmount + each.GetCharge;
end;
Rentals.Free;
//---结尾打印
Result := Result + 'Amount owed is ' + FloatToStr(totalAmount) + #13#10;
Result := Result + 'You earned ' + IntToStr(frequentRenterPoints) + ' frequent renter points';
end;
3、总量计算(去除临时变量)
statement函数中有两个临时变量totalAmount和frequentRenterPoints,两者都是用来从Customer对象相关的
Rental对象中获得某个总量。因为不论 ASCll 版或 HTML 版都需要这些总量。所以,我们运用“替换临时变量
(Replace Temp with Query)”和“查询方法(Query Method)”来取代临时变量。
通过去除临时变量,可以将冗长复杂的函数中的逻辑理顺,并使其更为清晰。如果系统中的其它地方需要这些信息
,也可以很轻松地将“查询方法”加入Customer类的公共接口。
3.1、总消费金额
使用“查询方法GetTotalCharge”去除“临时变量totalAmount”。
function TCustomer.GetTotalCharge: double;
{总消费金额}
var
Rentals: TEnumeration;
each: TRental;
begin
Result := 0;
//---
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
Result := Result + each.GetCharge;
end;
Rentals.Free;
end;
function TCustomer.Statement: string;
var
frequentRenterPoints: integer; //--常客积点
Rentals: TEnumeration;
each: TRental;
begin
Result := 'Rental Record for ' + self.Name + #13#10;
//---
frequentRenterPoints := 0;
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
//---累加常客积点
frequentRenterPoints := frequentRenterPoints + each.GetFrequentRenterPoints;
//---显示此笔租借数据
Result := Result + each.Movie.Title + ' ' + FloatToStr(each.GetCharge) + #13#10;
end;
Rentals.Free;
//---结尾打印
Result := Result + 'Amount owed is ' + FloatToStr(self.GetTotalCharge) + #13#10;
Result := Result + 'You earned ' + IntToStr(frequentRenterPoints) + ' frequent renter points';
end;
3.2、总常客积点
用“查询方法GetTotalFrequentRenterPoints”去除“临时变量frequentRenterPoints”。
function TCustomer.GetTotalFrequentRenterPoints: integer;
{总常客积点}
var
Rentals: TEnumeration;
each: TRental;
begin
Result := 0;
//---
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
Result := Result + each.GetFrequentRenterPoints;
end;
Rentals.Free;
end;
function TCustomer.Statement: string;
var
Rentals: TEnumeration;
each: TRental;
begin
Result := 'Rental Record for ' + self.Name + #13#10;
//---
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
//---显示此笔租借数据
Result := Result + each.Movie.Title + ' ' + FloatToStr(each.GetCharge) + #13#10;
end;
Rentals.Free;
//---结尾打印
Result := Result + 'Amount owed is ' + FloatToStr(self.GetTotalCharge) + #13#10;
Result := Result + 'You earned ' + IntToStr(self.GetTotalFrequentRenterPoints) + ' frequent
renter points';
end;
代码:
unit uMovie_Refactoring;
interface
uses
SysUtils,Contnrs;
const
REGULAR = 0;
NEW_RELEASE = 1;
CHILDRENS = 2;
type
TEnumeration = class
private
FList: TObjectList;
FIndex: integer;
public
constructor Create(const AList: TObjectList);
//---
function HasMoreElements: boolean;
function NextElement: TObject;
end;
//--影片
TMovie = class
private
FTitle: string; //--名称
FPriceCode: integer; //--价格(代号)
function GetPriceCode: integer;
function GetTitle: string;
procedure SetPriceCode(const Value: integer);
public
constructor Create(const ATitle: string; APriceCode: integer);
//---
property Title: string read GetTitle;
property PriceCode: integer read GetPriceCode write SetPriceCode;
end;
//--租赁
TRental = class
private
FMovie: TMovie;
FDaysRented: integer; //--租期
function GetDaysRented: integer;
function GetMovie: TMovie;
public
constructor Create(const AMovie: TMovie; ADaysRented: integer);
//---
function GetCharge: double; //---计算一笔租片费用
function GetFrequentRenterPoints: integer;
//---
property Movie: TMovie read GetMovie;
property DaysRented: integer read GetDaysRented;
end;
//--顾客
TCustomer = class
private
FName: string;
FRentals: TObjectList;
function GetName: string;
function GetTotalCharge: double;
function GetTotalFrequentRenterPoints: integer;
public
constructor Create(const AName: string);
destructor Destroy; override;
//---
procedure AddRental(arg: TRental);
function Statement: string; //--统计报表
//---
property Name: string read GetName;
end;
implementation
constructor TMovie.Create(const ATitle: string; APriceCode: integer);
begin
FTitle := ATitle;
FPriceCode := APriceCode;
end;
function TMovie.GetPriceCode: integer;
begin
Result := FPriceCode;
end;
function TMovie.GetTitle: string;
begin
Result := FTitle;
end;
procedure TMovie.SetPriceCode(const Value: integer);
begin
FPriceCode := Value;
end;
constructor TRental.Create(const AMovie: TMovie; ADaysRented: integer);
begin
FMovie := AMovie;
FDaysRented := ADaysRented;
end;
function TRental.GetCharge: double;
{计算一笔租片费用}
begin
Result := 0;
//---
case self.Movie.PriceCode of //--取得影片出租价格
REGULAR: //--普通片
begin
Result := Result + 2;
if self.DaysRented > 2 then
Result := Result + (self.DaysRented - 2) * 1.5;
end;
NEW_RELEASE: //--新片
begin
Result := Result + self.DaysRented * 3;
end;
CHILDRENS: //--儿童片
begin
Result := Result + 1.5;
if self.DaysRented > 3 then
Result := Result + (self.DaysRented - 3) * 1.5;
end;
end;
end;
function TRental.GetDaysRented: integer;
begin
Result := FDaysRented;
end;
function TRental.GetFrequentRenterPoints: integer;
{获取常客积点}
begin
if (self.Movie.PriceCode = NEW_RELEASE) and (self.DaysRented > 1) then
Result := 2
else
Result := 1;
end;
function TRental.GetMovie: TMovie;
begin
Result := FMovie;
end;
constructor TCustomer.Create(const AName: string);
begin
FName := AName;
FRentals := TObjectList.Create;
end;
destructor TCustomer.Destroy;
begin
FRentals.Free;
//---
inherited;
end;
procedure TCustomer.AddRental(arg: TRental);
begin
FRentals.Add(arg);
end;
function TCustomer.GetName: string;
begin
Result := FName;
end;
function TCustomer.GetTotalCharge: double;
{总消费金额}
var
Rentals: TEnumeration;
each: TRental;
begin
Result := 0;
//---
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
Result := Result + each.GetCharge;
end;
Rentals.Free;
end;
function TCustomer.GetTotalFrequentRenterPoints: integer;
{总常客积点}
var
Rentals: TEnumeration;
each: TRental;
begin
Result := 0;
//---
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
Result := Result + each.GetFrequentRenterPoints;
end;
Rentals.Free;
end;
function TCustomer.Statement: string;
var
Rentals: TEnumeration;
each: TRental;
begin
Result := 'Rental Record for ' + self.Name + #13#10;
//---
Rentals := TEnumeration.Create(FRentals);
while Rentals.HasMoreElements do
begin
each := TRental(Rentals.NextElement);
//---显示此笔租借数据
Result := Result + each.Movie.Title + ' ' + FloatToStr(each.GetCharge) + #13#10;
end;
Rentals.Free;
//---结尾打印
Result := Result + 'Amount owed is ' + FloatToStr(self.GetTotalCharge) + #13#10;
Result := Result + 'You earned ' + IntToStr(self.GetTotalFrequentRenterPoints) + ' frequent renter points';
end;
constructor TEnumeration.Create(const AList: TObjectList);
begin
FList := AList;
FIndex := 0;
end;
function TEnumeration.HasMoreElements: boolean;
begin
Result := FIndex < FList.Count;
end;
function TEnumeration.NextElement: TObject;
begin
Result := FList[FIndex];
FIndex := FIndex + 1;
end;
end.
《重构》 — Delphi示例:影片出租店程序(3、重构——分解并重组Statement)
最新推荐文章于 2021-05-26 02:43:14 发布