Файл: Учебное пособие для студентов Авторы А. Н. Вальвачев, К. А. Сурков, Д. А. Сурков, Ю. М. Четырько Содержание Содержание 1.doc
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 04.05.2024
Просмотров: 156
Скачиваний: 4
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
, которая иногда является достаточно сложной, может быть изменена с целью повышения эффективности работы программы. При этом потребуется переработать только методы чтения и записи значений свойств; внешний интерфейс класса не изменится.
3.6.2. Методы получения и установки значений свойств
Методы получения (чтения) и установки (записи) значений свойств подчиняются определенным правилам. Метод чтения свойства — это всегда функция, возвращающая значение того же типа, что и тип свойства. Метод записи свойства — это обязательно процедура, принимающая параметр того же типа, что и тип свойства. В остальных отношениях это обычные методы объекта. Примерами методов чтения и записи свойств являются методы GetItemCount и SetActive в классе TDelimitedReader:
type
TDelimitedReader = class
FActive: Boolean;
...
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
...
property Active: Boolean read FActive write SetActive;
property ItemCount: Integer read GetItemCount;
end;
Использование методов для получения и установки свойств позволяет проверить корректность значения свойства, сделать дополнительные вычисления, установить значения зависимых полей и т.д. Например, в методе SetActive вполне целесообразно осуществить проверку состояния файла (открыт или закрыт), чтобы избежать его повторного открытия или закрытия:
procedure TDelimitedReader.SetActive(const AActive: Boolean);
begin
if Active <> AActive then // Если состояние изменяется
begin
if AActive then
Reset(FFile) // Открытие файла
else
CloseFile(FFile); // Закрытие файла
FActive := AActive; // Сохранение состояния в поле
end;
end;
Наличие свойства Active позволяет нам отказаться от использования методов Open и Close, традиционных при работе с файлами. Согласитесь, что открывать и закрывать файл с помощью свойства Active гораздо удобнее и естественнее. Одновременно с этим свойство Active можно использовать и для проверки состояния файла (открыт или нет). Таким образом, для осуществления трех действий требуется всего лишь одно свойство! Это делает использование Ваших классов другими программистами более простым, поскольку им легче запомнить одно понятие Active, чем, например, три метода: Open, Close и IsOpen.
Значение свойства может не храниться, а вычисляться при каждом обращении к свойству. Примером является свойство ItemCount, значение которого вычисляется как Length(FItems).
3.6.3. Свойства-массивы
Кроме обычных свойств в объектах существуют свойства-массивы (array properties). Свойство-массив — это индексированное множество значений. Например, в классе TDelimitedReader множество элементов, выделенных из считанной строки, удобно представить в виде свойства-массива:
type
TDelimitedReader = class
...
FItems: array of string;
...
function GetItem(Index: Integer): string;
...
property Items[Index: Integer]: string read GetItem;
end;
function TDelimitedReader.GetItem(Index: Integer): string;
begin
Result := FItems[Index];
end;
Элементы массива Items можно только читать, поскольку класс TDelimitedReader предназначен только для чтения данных из файла.
В описании свойства-массива разрешено использовать только методы, но не поля. В этом состоит отличие свойства-массива от обычного свойства.
Основная выгода от применения свойства-массива — возможность выполнения итераций с помощью цикла for, например:
var
Reader: TDelimitedReader;
I: Integer;
...
for I := 0 to Reader.ItemCount - 1 do
Writeln(Reader.Items[I]);
...
Свойство-массив может быть многомерным. В этом случае методы чтения и записи элементов должны иметь столько же индексных параметров соответствующих типов, что и свойство-массив.
Свойства-массивы имеют два важных отличия от обычных массивов:
Reader.Items['FirstName'] := 'Alexander';
3.6.4. Свойство-массив как основное свойство объекта
Свойство-массив можно сделать основным свойством объектов данного класса. Для этого в описание свойства добавляется слово default:
type
TDelimitedReader = class
...
property Items[Index: Integer]: string read GetItem; default;
...
end;
Такое объявление свойства Items позволяет рассматривать сам объект класса TDelimitedReader как массив и опускать имя свойства-массива при обращении к нему из программы, например:
var
R: TDelimitedReader;
I: Integer;
...
for I := 0 to R.ItemCount - 1 do
Writeln(R[I]);
...
Следует помнить, что только свойства-массивы могут быть основными свойствами объектов; для обычных свойств это недопустимо.
3.6.5. Методы, обслуживающие несколько свойств
Один и тот же метод может использоваться для получения (установки) значений нескольких свойств одного типа. В этом случае каждому свойству назначается целочисленный индекс, который передается в метод чтения (записи) первым параметром.
В следующем примере уже известный Вам метод GetItem обслуживает три свойства: FirstName, LastName и Phone:
type
TDelimitedReader = class
...
property FirstName: string index 0 read GetItem;
property LastName: string index 1 read GetItem;
property Phone: string index 2 read GetItem;
end;
Обращения к свойствам FirstName, LastName и Phone заменяются компилятором на вызовы одного и того же метода GetItem, но с разными значениями параметра Index:
var
Reader: TDelimitedReader;
...
Writeln(Reader.FirstName); // Эквивалентно: Writeln(Reader.GetItem(0));
Writeln(Reader.LastName); // Эквивалентно: Writeln(Reader.GetItem(1));
Writeln(Reader.Phone); // Эквивалентно: Writeln(Reader.GetItem(2));
...
Обратите внимание, что метод GetItem обслуживает как свойство-массив Items, так и свойства FirstName, LastName и Phone. Удобно, не правда ли!
Перед тем, как перейти к более сложным понятиям ООП, приведем полную реализацию класса TDelimitedReader. Настоятельно рекомендуем Вам внимательно ознакомиться с этой реализацией, поскольку в ней сведено воедино все то, о чем говорилось в предыдущих разделах.
type
TDelimitedReader = class
// Поля
FFile: TextFile;
FItems: array of string;
FActive: Boolean;
FDelimiter: Char;
// Методы чтения и записи свойств
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
function GetEndOfFile: Boolean;
function GetItem(Index: Integer): string;
// Методы
procedure PutItem(Index: Integer; const Item: string);
function ParseLine(const Line: string): Integer;
function NextLine: Boolean;
// Конструкторы и деструкторы
constructor Create(const FileName: string; const ADelimiter: Char = ';');
destructor Destroy; override;
// Свойства
property Active: Boolean read FActive write SetActive;
property Items[Index: Integer]: string read GetItem; default;
property ItemCount: Integer read GetItemCount;
property EndOfFile: Boolean read GetEndOfFile;
property Delimiter: Char read FDelimiter;
end;
{ TDelimitedReader }
constructor TDelimitedReader.Create(const FileName: string;
const ADelimiter: Char = ';');
begin
AssignFile(FFile, FileName);
FActive := False;
FDelimiter := ADelimiter;
end;
destructor TDelimitedReader.Destroy;
begin
Active := False;
end;
function TDelimitedReader.GetEndOfFile: Boolean;
begin
Result := Eof(FFile);
end;
function TDelimitedReader.GetItem(Index: Integer): string;
begin
Result := FItems[Index];
end;
function TDelimitedReader.GetItemCount: Integer;
begin
Result := Length(FItems);
end;
function TDelimitedReader.NextLine: Boolean;
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then // Если не достигнут конец файла
begin
Readln(FFile, S); // Чтение очередной строки из файла
N := ParseLine(S); // Разбор считанной строки
if N <> ItemCount then
SetLength(FItems, N); // Отсечение массива (если необходимо)
end;
end;
function TDelimitedReader.ParseLine(const Line: string): Integer;
var
S: string;
P: Integer;
begin
S := Line;
Result := 0;
repeat
P := Pos(Delimiter, S); // Поиск разделителя
if P = 0 then // Если разделитель не найден, то считается, что
P := Length(S) + 1; // разделитель находится за последним символом
PutItem(Result, Copy(S, 1, P - 1)); // Установка элемента
Delete(S, 1, P); // Удаление элемента из строки
Result := Result + 1; // Переход к следующему элементу
until S = ''; // Пока в строке есть символы
end;
procedure TDelimitedReader.PutItem(Index: Integer; const Item: string);
begin
if Index > High(FItems) then // Если индекс выходит за границы массива,
SetLength(FItems, Index + 1); // то увеличение размера массива
FItems[Index] := Item; // Установка соответствующего элемента
end;
procedure TDelimitedReader.SetActive(const AActive: Boolean);
begin
if Active <> AActive then // Если состояние изменяется
begin
if AActive then
Reset(FFile) // Открытие файла
else
CloseFile(FFile); // Закрытие файла
FActive := AActive; // Сохранение состояния в поле
end;
end;
3.7. Наследование
3.7.1. Понятие наследования
Классы инкапсулируют (т.е. включают в себя) поля, методы и свойства; это их первая черта. Следующая не менее важная черта классов — способность наследовать поля, методы и свойства других классов. Чтобы пояснить сущность наследования обратимся к примеру с читателем текстовых файлов в формате "delimited text".
Класс TDelimitedReader описывает объекты для чтения из текстового файла элементов, разделенных некоторым символом. Он не пригоден для чтения элементов, хранящихся в другом формате, например в формате с фиксированным количеством символов для каждого элемента. Для этого необходим другой класс:
type
TFixedReader = class
private
// Поля
FFile: TextFile;
FItems: array of string;
FActive: Boolean;
FItemWidths: array of Integer;
// Методы чтения и записи свойств
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
function GetEndOfFile: Boolean;
function GetItem(Index: Integer): string;
// Методы
procedure PutItem(Index: Integer; const Item: string);
function ParseLine(const Line: string): Integer;
function NextLine: Boolean;
// Конструкторы и деструкторы
constructor Create(const FileName: string;
const AItemWidths: array of Integer);
destructor Destroy; override;
// Свойства
property Active: Boolean read FActive write SetActive;
property Items[Index: Integer]: string read GetItem; default;
property ItemCount: Integer read GetItemCount;
property EndOfFile: Boolean read GetEndOfFile;
end;
{ TFixedReader }
constructor TFixedReader.Create(const FileName: string;
const AItemWidths: array of Integer);
var
I: Integer;
begin
AssignFile(FFile, FileName);
FActive := False;
// Копирование AItemWidths в FItemWidths
SetLength(FItemWidths, Length(AItemWidths));
for I := 0 to High(AItemWidths) do
FItemWidths[I] := AItemWidths[I];
end;
destructor TFixedReader.Destroy;
begin
Active := False;
end;
function TFixedReader.GetEndOfFile: Boolean;
begin
Result := Eof(FFile);
end;
function TFixedReader.GetItem(Index: Integer): string;
begin
Result := FItems[Index];
end;
function TFixedReader.GetItemCount: Integer;
begin
Result := Length(FItems);
end;
function TFixedReader.NextLine: Boolean;
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then // Если не достигнут конец файла
begin
Readln(FFile, S); // Чтение очередной строки из файла
N := ParseLine(S); // Разбор считанной строки
if N <> ItemCount then
SetLength(FItems, N); // Отсечение массива (если необходимо)
end;
end;
function TFixedReader.ParseLine(const Line: string): Integer;
var
I, P: Integer;
begin
P := 1;
for I := 0 to High(FItemWidths) do
begin
PutItem(I, Copy(Line, P, FItemWidths[I])); // Установка элемента
P := P + FItemWidths[I]; // Переход к следующему элементу
end;
Result := Length(FItemWidths); // Количество элементов постоянно
end;
procedure TFixedReader.PutItem(Index: Integer; const Item: string);
begin
if Index > High(FItems) then // Если индекс выходит за границы массива,
SetLength(FItems, Index + 1); // то увеличение размера массива
FItems[Index] := Item; // Установка соответствующего элемента
end;
procedure TFixedReader.SetActive(const AActive: Boolean);
begin
if Active <> AActive then // Если состояние изменяется
begin
if AActive then
Reset(FFile) // Открытие файла
else
CloseFile(FFile); // Закрытие файла
FActive := AActive; // Сохранение состояния в поле
end;
end;
Поля, свойства и методы класса TFixedReader практически полностью аналогичны тем, что определены в классе TDelimitedReader. Отличие состоит в отсутствии свойства Delimiter, наличии поля FItemWidths (для хранения размеров элементов), другой реализации метода ParseLine и немного отличающемся конструкторе. Если в будущем появится класс для чтения элементов из файла еще одного формата (например, зашифрованного текста), то придется снова определять общие для всех классов поля, методы и свойства. Чтобы избавиться от дублирования общих атрибутов (полей, свойств и методов) при определении новых классов, воспользуемся механизмом наследования. Прежде всего, выделим в отдельный класс TTextReader общие атрибуты всех классов, предназначенных для чтения элементов из текстовых файлов. Реализация методов TTextReader, кроме метода ParseLine, полностью идентична реализации TDelimitedReader, приведенной в предыдущем разделе.
3.6.2. Методы получения и установки значений свойств
Методы получения (чтения) и установки (записи) значений свойств подчиняются определенным правилам. Метод чтения свойства — это всегда функция, возвращающая значение того же типа, что и тип свойства. Метод записи свойства — это обязательно процедура, принимающая параметр того же типа, что и тип свойства. В остальных отношениях это обычные методы объекта. Примерами методов чтения и записи свойств являются методы GetItemCount и SetActive в классе TDelimitedReader:
type
TDelimitedReader = class
FActive: Boolean;
...
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
...
property Active: Boolean read FActive write SetActive;
property ItemCount: Integer read GetItemCount;
end;
Использование методов для получения и установки свойств позволяет проверить корректность значения свойства, сделать дополнительные вычисления, установить значения зависимых полей и т.д. Например, в методе SetActive вполне целесообразно осуществить проверку состояния файла (открыт или закрыт), чтобы избежать его повторного открытия или закрытия:
procedure TDelimitedReader.SetActive(const AActive: Boolean);
begin
if Active <> AActive then // Если состояние изменяется
begin
if AActive then
Reset(FFile) // Открытие файла
else
CloseFile(FFile); // Закрытие файла
FActive := AActive; // Сохранение состояния в поле
end;
end;
Наличие свойства Active позволяет нам отказаться от использования методов Open и Close, традиционных при работе с файлами. Согласитесь, что открывать и закрывать файл с помощью свойства Active гораздо удобнее и естественнее. Одновременно с этим свойство Active можно использовать и для проверки состояния файла (открыт или нет). Таким образом, для осуществления трех действий требуется всего лишь одно свойство! Это делает использование Ваших классов другими программистами более простым, поскольку им легче запомнить одно понятие Active, чем, например, три метода: Open, Close и IsOpen.
Значение свойства может не храниться, а вычисляться при каждом обращении к свойству. Примером является свойство ItemCount, значение которого вычисляется как Length(FItems).
3.6.3. Свойства-массивы
Кроме обычных свойств в объектах существуют свойства-массивы (array properties). Свойство-массив — это индексированное множество значений. Например, в классе TDelimitedReader множество элементов, выделенных из считанной строки, удобно представить в виде свойства-массива:
type
TDelimitedReader = class
...
FItems: array of string;
...
function GetItem(Index: Integer): string;
...
property Items[Index: Integer]: string read GetItem;
end;
function TDelimitedReader.GetItem(Index: Integer): string;
begin
Result := FItems[Index];
end;
Элементы массива Items можно только читать, поскольку класс TDelimitedReader предназначен только для чтения данных из файла.
В описании свойства-массива разрешено использовать только методы, но не поля. В этом состоит отличие свойства-массива от обычного свойства.
Основная выгода от применения свойства-массива — возможность выполнения итераций с помощью цикла for, например:
var
Reader: TDelimitedReader;
I: Integer;
...
for I := 0 to Reader.ItemCount - 1 do
Writeln(Reader.Items[I]);
...
Свойство-массив может быть многомерным. В этом случае методы чтения и записи элементов должны иметь столько же индексных параметров соответствующих типов, что и свойство-массив.
Свойства-массивы имеют два важных отличия от обычных массивов:
-
их индексы не ограничиваются диапазоном и могут иметь любой тип данных, а не только Integer. Например, можно создать свойство-массив, в котором индексами будут строки. Обращение к такому свойству могло бы выглядеть примерно так:
Reader.Items['FirstName'] := 'Alexander';
-
операции целиком со всем свойством-массивом запрещены; разрешены операции только с его элементами.
3.6.4. Свойство-массив как основное свойство объекта
Свойство-массив можно сделать основным свойством объектов данного класса. Для этого в описание свойства добавляется слово default:
type
TDelimitedReader = class
...
property Items[Index: Integer]: string read GetItem; default;
...
end;
Такое объявление свойства Items позволяет рассматривать сам объект класса TDelimitedReader как массив и опускать имя свойства-массива при обращении к нему из программы, например:
var
R: TDelimitedReader;
I: Integer;
...
for I := 0 to R.ItemCount - 1 do
Writeln(R[I]);
...
Следует помнить, что только свойства-массивы могут быть основными свойствами объектов; для обычных свойств это недопустимо.
3.6.5. Методы, обслуживающие несколько свойств
Один и тот же метод может использоваться для получения (установки) значений нескольких свойств одного типа. В этом случае каждому свойству назначается целочисленный индекс, который передается в метод чтения (записи) первым параметром.
В следующем примере уже известный Вам метод GetItem обслуживает три свойства: FirstName, LastName и Phone:
type
TDelimitedReader = class
...
property FirstName: string index 0 read GetItem;
property LastName: string index 1 read GetItem;
property Phone: string index 2 read GetItem;
end;
Обращения к свойствам FirstName, LastName и Phone заменяются компилятором на вызовы одного и того же метода GetItem, но с разными значениями параметра Index:
var
Reader: TDelimitedReader;
...
Writeln(Reader.FirstName); // Эквивалентно: Writeln(Reader.GetItem(0));
Writeln(Reader.LastName); // Эквивалентно: Writeln(Reader.GetItem(1));
Writeln(Reader.Phone); // Эквивалентно: Writeln(Reader.GetItem(2));
...
Обратите внимание, что метод GetItem обслуживает как свойство-массив Items, так и свойства FirstName, LastName и Phone. Удобно, не правда ли!
Перед тем, как перейти к более сложным понятиям ООП, приведем полную реализацию класса TDelimitedReader. Настоятельно рекомендуем Вам внимательно ознакомиться с этой реализацией, поскольку в ней сведено воедино все то, о чем говорилось в предыдущих разделах.
type
TDelimitedReader = class
// Поля
FFile: TextFile;
FItems: array of string;
FActive: Boolean;
FDelimiter: Char;
// Методы чтения и записи свойств
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
function GetEndOfFile: Boolean;
function GetItem(Index: Integer): string;
// Методы
procedure PutItem(Index: Integer; const Item: string);
function ParseLine(const Line: string): Integer;
function NextLine: Boolean;
// Конструкторы и деструкторы
constructor Create(const FileName: string; const ADelimiter: Char = ';');
destructor Destroy; override;
// Свойства
property Active: Boolean read FActive write SetActive;
property Items[Index: Integer]: string read GetItem; default;
property ItemCount: Integer read GetItemCount;
property EndOfFile: Boolean read GetEndOfFile;
property Delimiter: Char read FDelimiter;
end;
{ TDelimitedReader }
constructor TDelimitedReader.Create(const FileName: string;
const ADelimiter: Char = ';');
begin
AssignFile(FFile, FileName);
FActive := False;
FDelimiter := ADelimiter;
end;
destructor TDelimitedReader.Destroy;
begin
Active := False;
end;
function TDelimitedReader.GetEndOfFile: Boolean;
begin
Result := Eof(FFile);
end;
function TDelimitedReader.GetItem(Index: Integer): string;
begin
Result := FItems[Index];
end;
function TDelimitedReader.GetItemCount: Integer;
begin
Result := Length(FItems);
end;
function TDelimitedReader.NextLine: Boolean;
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then // Если не достигнут конец файла
begin
Readln(FFile, S); // Чтение очередной строки из файла
N := ParseLine(S); // Разбор считанной строки
if N <> ItemCount then
SetLength(FItems, N); // Отсечение массива (если необходимо)
end;
end;
function TDelimitedReader.ParseLine(const Line: string): Integer;
var
S: string;
P: Integer;
begin
S := Line;
Result := 0;
repeat
P := Pos(Delimiter, S); // Поиск разделителя
if P = 0 then // Если разделитель не найден, то считается, что
P := Length(S) + 1; // разделитель находится за последним символом
PutItem(Result, Copy(S, 1, P - 1)); // Установка элемента
Delete(S, 1, P); // Удаление элемента из строки
Result := Result + 1; // Переход к следующему элементу
until S = ''; // Пока в строке есть символы
end;
procedure TDelimitedReader.PutItem(Index: Integer; const Item: string);
begin
if Index > High(FItems) then // Если индекс выходит за границы массива,
SetLength(FItems, Index + 1); // то увеличение размера массива
FItems[Index] := Item; // Установка соответствующего элемента
end;
procedure TDelimitedReader.SetActive(const AActive: Boolean);
begin
if Active <> AActive then // Если состояние изменяется
begin
if AActive then
Reset(FFile) // Открытие файла
else
CloseFile(FFile); // Закрытие файла
FActive := AActive; // Сохранение состояния в поле
end;
end;
3.7. Наследование
3.7.1. Понятие наследования
Классы инкапсулируют (т.е. включают в себя) поля, методы и свойства; это их первая черта. Следующая не менее важная черта классов — способность наследовать поля, методы и свойства других классов. Чтобы пояснить сущность наследования обратимся к примеру с читателем текстовых файлов в формате "delimited text".
Класс TDelimitedReader описывает объекты для чтения из текстового файла элементов, разделенных некоторым символом. Он не пригоден для чтения элементов, хранящихся в другом формате, например в формате с фиксированным количеством символов для каждого элемента. Для этого необходим другой класс:
type
TFixedReader = class
private
// Поля
FFile: TextFile;
FItems: array of string;
FActive: Boolean;
FItemWidths: array of Integer;
// Методы чтения и записи свойств
procedure SetActive(const AActive: Boolean);
function GetItemCount: Integer;
function GetEndOfFile: Boolean;
function GetItem(Index: Integer): string;
// Методы
procedure PutItem(Index: Integer; const Item: string);
function ParseLine(const Line: string): Integer;
function NextLine: Boolean;
// Конструкторы и деструкторы
constructor Create(const FileName: string;
const AItemWidths: array of Integer);
destructor Destroy; override;
// Свойства
property Active: Boolean read FActive write SetActive;
property Items[Index: Integer]: string read GetItem; default;
property ItemCount: Integer read GetItemCount;
property EndOfFile: Boolean read GetEndOfFile;
end;
{ TFixedReader }
constructor TFixedReader.Create(const FileName: string;
const AItemWidths: array of Integer);
var
I: Integer;
begin
AssignFile(FFile, FileName);
FActive := False;
// Копирование AItemWidths в FItemWidths
SetLength(FItemWidths, Length(AItemWidths));
for I := 0 to High(AItemWidths) do
FItemWidths[I] := AItemWidths[I];
end;
destructor TFixedReader.Destroy;
begin
Active := False;
end;
function TFixedReader.GetEndOfFile: Boolean;
begin
Result := Eof(FFile);
end;
function TFixedReader.GetItem(Index: Integer): string;
begin
Result := FItems[Index];
end;
function TFixedReader.GetItemCount: Integer;
begin
Result := Length(FItems);
end;
function TFixedReader.NextLine: Boolean;
var
S: string;
N: Integer;
begin
Result := not EndOfFile;
if Result then // Если не достигнут конец файла
begin
Readln(FFile, S); // Чтение очередной строки из файла
N := ParseLine(S); // Разбор считанной строки
if N <> ItemCount then
SetLength(FItems, N); // Отсечение массива (если необходимо)
end;
end;
function TFixedReader.ParseLine(const Line: string): Integer;
var
I, P: Integer;
begin
P := 1;
for I := 0 to High(FItemWidths) do
begin
PutItem(I, Copy(Line, P, FItemWidths[I])); // Установка элемента
P := P + FItemWidths[I]; // Переход к следующему элементу
end;
Result := Length(FItemWidths); // Количество элементов постоянно
end;
procedure TFixedReader.PutItem(Index: Integer; const Item: string);
begin
if Index > High(FItems) then // Если индекс выходит за границы массива,
SetLength(FItems, Index + 1); // то увеличение размера массива
FItems[Index] := Item; // Установка соответствующего элемента
end;
procedure TFixedReader.SetActive(const AActive: Boolean);
begin
if Active <> AActive then // Если состояние изменяется
begin
if AActive then
Reset(FFile) // Открытие файла
else
CloseFile(FFile); // Закрытие файла
FActive := AActive; // Сохранение состояния в поле
end;
end;
Поля, свойства и методы класса TFixedReader практически полностью аналогичны тем, что определены в классе TDelimitedReader. Отличие состоит в отсутствии свойства Delimiter, наличии поля FItemWidths (для хранения размеров элементов), другой реализации метода ParseLine и немного отличающемся конструкторе. Если в будущем появится класс для чтения элементов из файла еще одного формата (например, зашифрованного текста), то придется снова определять общие для всех классов поля, методы и свойства. Чтобы избавиться от дублирования общих атрибутов (полей, свойств и методов) при определении новых классов, воспользуемся механизмом наследования. Прежде всего, выделим в отдельный класс TTextReader общие атрибуты всех классов, предназначенных для чтения элементов из текстовых файлов. Реализация методов TTextReader, кроме метода ParseLine, полностью идентична реализации TDelimitedReader, приведенной в предыдущем разделе.