7.3.1 一个私有数据的例子
作为使用这些访问说明符实现封装的示例,考虑一下TDate类的新版本:
TDate = class
private
Month, Day, Year: Integer;
public
procedure SetValue(M, D, Y: Integer);
function LeapYear: Boolean;
function GetText: string;
procedure Increase;
end;
在这个版本中,字段现在被声明为private
,并且有了一些新方法。第一个方法是 GetText
,它是一个返回包含日期的字符串的函数。您可能会考虑添加其他函数,如 GetDay
、GetMonth
和 GetYear
,这些函数只需返回相应的私有数据,但并不总是需要类似的直接数据访问函数。为每个字段提供访问函数可能会降低封装性、削弱抽象性,并使以后修改类的内部实现变得更加困难。只有当访问函数是类的逻辑接口的一部分时,才应该提供访问函数,而不是因为有匹配的字段。
第二个新方法是Increase过程,它将日期增加一天。这并不简单,因为你需要考虑各种月份的不同长度以及闰年和非闰年。为了简化编码,我将更改类的内部实现,使用Object Pascal
的TDateTime
类型作为内部实现。所以实际的类将更改为你可以在Dates2示例中找到的以下代码:
type
TDate = class
private
FDate: TDateTime;
public
procedure SetValue(M, D, Y: Integer);
function LeapYear: Boolean;
function GetText: string;
procedure Increase;
end;
请注意,由于对类的唯一更改在类的私有部分,因此你不必修改任何已使用该类的现有程序。这就是封装的优势!
注解:在这个新版本的类中,字段的标识符以字母 "F "开头。这是 Object Pascal 中一个相当常见的约定,我在书中一般也会使用这个约定。
在本节的最后,让我通过列出类方法的源代码来结束对该项目的描述,这些方法依赖于一些系统函数来将日期映射到内部结构,反之亦然:
procedure TDate.SetValue(M, D, Y: Integer);
begin
FDate := EncodeDate(Y, M, D);
end;
function TDate.GetText: string;
begin
Result := DateToStr(FDate);
end;
procedure TDate.Increase;
begin
FDate := FDate + 1;
end;
function TDate.LeapYear: Boolean;
begin
// 调用SysUtils中的IsLeapYear和DateUtils中的YearOf
Result := IsLeapYear(YearOf(FDate));
end;
还请注意,使用该类的代码不能再引用 Year 值,而只能在其方法允许的范围内返回日期对象的信息:
var
ADay: TDate;
begin
// 创建
ADay := TDate.Create;
// 使用
ADay.SetValue(1, 1, 2020);
ADay.Increase;
if ADay.LeapYear then
Show('Leap year: ' + ADay.GetText);
// 释放内存
ADay.Free;
输出与之前没有太大的不同:
Leap year: 1/2/2020
请注意,你的输出可能有所不同,因为日期的格式根据系统的语言环境设置而变化。