При разработке приложений на Delphi часто возникает необходимость создавать классы с большим количеством полей. В этой статье мы рассмотрим различные подходы к работе с такими классами, включая использование свойств (properties), сеттеров и геттеров, а также объявление записей внутри классов.
Основы работы со свойствами в классах
Свойства (properties) в Delphi предоставляют удобный способ доступа к данным класса, позволяя скрыть реализацию и добавить дополнительную логику при чтении или записи значений.
Рассмотрим базовый пример класса с одним полем:
type
TEmployee = class
private
FName: string;
function GetName: string;
procedure SetName(const Value: string);
public
property Name: string read GetName write SetName;
end;
implementation
function TEmployee.GetName: string;
begin
Result := FName;
end;
procedure TEmployee.SetName(const Value: string);
begin
if Value = '' then
raise Exception.Create('Name must have a value');
FName := Value;
end;
В этом примере мы видим: - Приватное поле FName для хранения данных - Методы GetName и SetName для доступа к полю - Публичное свойство Name, которое использует эти методы
Проблема множества полей
Когда в классе много полей (например, 50), написание отдельных геттеров и сеттеров для каждого становится утомительным. Рассмотрим возможные решения.
Решение 1: Прямой доступ к полям
Если ваши поля не требуют дополнительной логики при чтении/записи, можно сделать их публичными:
type
TEmployee = class
public
Name: string;
Salary: Currency;
// и другие поля
end;
Преимущества: - Простота реализации - Нет необходимости в дополнительных методах
Недостатки: - Невозможно добавить проверки при установке значений - Меньшая гибкость для будущих изменений
Решение 2: Прямое связывание свойств с полями
Можно использовать свойства, которые напрямую обращаются к полям:
type
TEmployee = class
private
FName: string;
FSalary: Currency;
public
property Name: string read FName write FName;
property Salary: Currency read FSalary write FSalary;
end;
Этот подход сохраняет возможность в будущем добавить методы доступа, не меняя интерфейс класса.
Решение 3: Индексированные свойства
Если поля однотипные, можно использовать индексированные свойства:
type
TEmployee = class
private
FNames: array[0..49] of string;
function GetName(Index: Integer): string;
procedure SetName(Index: Integer; const Value: string);
public
property Names[Index: Integer]: string read GetName write SetName;
end;
implementation
function TEmployee.GetName(Index: Integer): string;
begin
Result := FNames[Index];
end;
procedure TEmployee.SetName(Index: Integer; const Value: string);
begin
if Value = '' then
raise Exception.Create('Value must not be empty');
FNames[Index] := Value;
end;
Решение 4: Использование констант для индексов
Для разнотипных полей можно использовать константы и один метод доступа:
const
NameIndex = 0;
SalaryIndex = 1;
type
TEmployee = class
private
FValues: array[0..49] of string;
function GetValue(Index: Integer): string;
procedure SetValue(Index: Integer; const Value: string);
public
property Name: string index NameIndex read GetValue write SetValue;
property Salary: string index SalaryIndex read GetValue write SetValue;
end;
Решение 5: Обобщенные методы (Generics)
В современных версиях Delphi можно использовать Generics для создания универсальных методов доступа:
type
TEmployee = class
private
FName: string;
FSalary: Currency;
FAcNo: Integer;
function Gett<T>(var Field: T): T;
procedure Sett<T>(var Field: T; const Value: T);
public
property Name: string read FName write SetName;
procedure SetName(const Value: string);
end;
implementation
function TEmployee.Gett<T>(var Field: T): T;
begin
Result := Field;
end;
procedure TEmployee.Sett<T>(var Field: T; const Value: T);
begin
var lComparer := TEqualityComparer<T>.Default;
if lComparer.Equals(Value, Default(T)) then
raise Exception.Create('Field must have a non-default value')
else
Field := Value;
end;
procedure TEmployee.SetName(const Value: string);
begin
Sett<string>(FName, Value);
end;
Объявление записей внутри классов
Delphi позволяет объявлять записи (records) внутри классов, что может быть полезно для группировки связанных полей:
type
TEmployee = class
private
FPersonalDetails: record
FirstName: string;
LastName: string;
BirthDate: TDate;
end;
FWorkDetails: record
Position: string;
Department: string;
HireDate: TDate;
end;
public
type
TContactInfo = record
Phone: string;
Email: string;
Address: string;
end;
var
Contact: TContactInfo;
end;
Такой подход улучшает организацию кода и делает его более читаемым.
Рекомендации по проектированию классов
Принцип минимального доступа: Делайте поля приватными, предоставляя доступ только через свойства.
Группировка полей: Объединяйте связанные поля в записи или вложенные классы.
Инициализация в конструкторе: Для обязательных полей устанавливайте значения в конструкторе.
Использование свойств: Свойства предоставляют гибкость для будущих изменений.
Документирование: Комментируйте назначение каждого поля и свойства.
Пример комплексного класса
type
TEmployee = class
private
FBasicInfo: record
FName: string;
FEmail: string;
FPhone: string;
end;
FFinancialInfo: record
FSalary: Currency;
FBankAccount: string;
FTaxCode: string;
end;
function GetName: string;
procedure SetName(const Value: string);
function GetEmail: string;
procedure SetEmail(const Value: string);
public
constructor Create(const Name, Email: string);
property Name: string read GetName write SetName;
property Email: string read GetEmail write SetEmail;
property Phone: string read FBasicInfo.FPhone write FBasicInfo.FPhone;
property Salary: Currency read FFinancialInfo.FSalary write FFinancialInfo.FSalary;
end;
implementation
constructor TEmployee.Create(const Name, Email: string);
begin
inherited Create;
SetName(Name);
SetEmail(Email);
end;
function TEmployee.GetName: string;
begin
Result := FBasicInfo.FName;
end;
procedure TEmployee.SetName(const Value: string);
begin
if Value = '' then
raise Exception.Create('Name cannot be empty');
FBasicInfo.FName := Value;
end;
function TEmployee.GetEmail: string;
begin
Result := FBasicInfo.FEmail;
end;
procedure TEmployee.SetEmail(const Value: string);
begin
if not Value.Contains('@') then
raise Exception.Create('Invalid email format');
FBasicInfo.FEmail := Value;
end;
Заключение
При работе с классами, содержащими множество полей, важно выбрать подход, который обеспечивает баланс между удобством разработки и гибкостью к изменениям. Использование свойств, группировка полей в записи и применение современных возможностей Delphi, таких как Generics, помогут создать поддерживаемый и расширяемый код.
Помните, что нет единственно правильного решения - выбор подхода зависит от конкретных требований вашего проекта. Начинайте с простых решений и усложняйте их только тогда, когда это действительно необходимо.
Статья описывает различные подходы к созданию классов с множеством полей в Delphi, включая использование свойств, сеттеров и геттеров, а также группировку полей в записи для улучшения организации кода.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.