14. Классы

Класс в Object Pascal – это специальный тип, который содержит поля, методы и свойства. Класс и объект являются ключевыми понятиями в объектно-ориентированном программировании (ООП). Класс определяет структуру и функциональность, а объект является конкретным экземпляром класса, который работает в соответствии с логикой заданной классом.

Класс представляет собой относительно самостоятельную часть кода, выполняющую определенную задачу. Например, класс TStringList реализует работу со списками строк, TFileStream – обеспечивает работу с файлами, TdxForm – обеспечивает работу с полями формы, автоматические вычисления и многое другое. Классы позволяют структурировать код, сделать его лучше читаемым, обеспечивают повторное использование кода, облегчают сопровождение программ. Программист может использовать сторонние классы, не вникая в их реализацию. Ему достаточно изучить интерфейсную часть класса: набор методов и свойств. Методы выполняют какие-либо действия, свойства влияют на характеристики и поведение объекта. Например, метод SaveToFile класса TStringList сохраняет список строк в файл на диске, свойство Caption класса TdxButton позволяет узнать или поменять надпись на кнопке. Пример использования класса:

var
  SL: TStringList;
begin
  // До создания объекта переменная SL = nil.
  SL := TStringList.Create;
  SL.Delimiter := ' ';
  SL.DelimitedText := 'Один Два Три Четыре Пять';
  Debug(SL.Count);
  SL.SaveToFile('d:\data.txt');
  SL.Free;
  // После удаления объекта переменная SL содержит указатель на несуществующий объект.
  // Попытка обратиться к методу или свойству объекта приведет к ошибке 
  // "Access violation". Помещаем в переменную нулевой указатель, чтобы можно было понять
  // существует объект или нет.
  SL := nil;	
  if SL <> nil thenelseend;

Объект создается с помощью конструктора класса. Как правило, конструктор имеет название Create. При вызове конструктора происходит выделение памяти для объекта и другие подготовительные действия, заданные разработчиком класса. В переменной сохраняется ссылку на объект – адрес участка памяти. До вызова конструктора переменная содержит неопределенный указатель, обозначается ключевым словом nil. После создания объекта можно обращаться к его методам и свойствам через точку. Закончив работу с объектом, нужно освободить занятую объектом память или, как говорят по-другому, уничтожить объект. Делается это методом Free.

Стоит обратить внимание, что при уничтожении объекта переменная по-прежнему содержит ссылку, но уже на несуществующий объект. Попытка обратиться к методу или свойству несуществующего объекта приведет к ошибке «Access violation».

Присваивайте переменной класса значение nil, чтобы можно было понять существует объект или нет.

Объект можно сравнивать с nil или с другим объектом.

Основные принципы ООП

К основным принципам ООП относят: инкапсуляцию, наследование, полиморфизм.

Инкапсуляция – это скрытие реализации класса, предоставление только открытых методов и свойств для работы с ним. Разработчику необязательно вникать во внутренние механизмы класса, ему достаточно знать назначение его открытых методов и свойств. Разработчики классов могут разграничивать доступ к полям, методам и свойствам класса. Есть три уровня доступа: приватный, защищенный и публичный. К приватным данным класса имеет доступ только сам класс. К защищенным данным имеют доступ потомки класса. К публичным данным имеет доступ весь код.

Наследование позволяет создать новый класс на основе существующего класса. Новый класс называют потомком, а наследуемый класс – базовым классом, предком, родительским. Новый класс может расширять функционал базового класса и переопределять существующий функционал. Создаются целые иерархии классов, где базовые классы обладают некоторыми методами и свойствами, которые общие для всех потомков. Взять, к примеру, классы LCL. Элементы графического интерфейса представлены такими классами, как TEdit – поле для ввода текста, TLabel - метка, TButton – кнопка. Каждый из них имеет специфические свойства и методы. Но все они, по сути, являются элементами управления, которые имеют положение, размер, цвет и т. д. И чтобы не повторять для каждого класса одни и те же свойства, а также задать общее для всех поведение, они наследуют общие свойства и методы от базового класса TControl.

Полиморфизм – это когда базовый объект «знает», метод какого потомка надо вызвать.

procedure RepaintAllControls(Container: TWinControl);
var
  i: Integer;
  C: TControl;
begin
  for i := 0 to Container.ControlCount1 do
  begin
    C := Container.Controls[i];
    C.Repaint;			// Перерисовываем компонент
  end;
end;

В данном примере выполняется перерисовка (обновление внешнего вида) компонентов некоторого контейнера. У базового класса всех визуальных компонентов TControl есть метод Repaint, который переопределяется в потомках, т. е. в каждом потомке реализуется свой алгоритм рисования. Но нам не обязательно вызывать метод Repaint конкретного потомка, достаточно вызвать метод базового класса, программа сама разберется метод какого потомка нужно вызвать.

Еще про объекты

Когда создается объект некоторого класса, ссылка на него может быть записана в переменную базового класса. Например:

var
  SL: TStrings;
begin
  SL := TStringList.Create;end;

Переменная базового класса не дает доступа к дополнительным свойствам и методам потомка. Для этого нужно выполнить преобразование типа. Делается это следующим образом:

TStringList(SL).Sort;

Если переменная содержит ссылку на несуществующий объект, то появится ошибка «cannot cast an object». Если переменная содержит nil, то появиться ошибка «null pointer exception» или «could not call proc». Если результат преобразования сохранить в другой переменной, то в случае с nil ошибки не будет, другая переменная тоже будет содержать nil:

var
  SL: TStrings;
  SL2: TStringList;
begin
  SL2 := TStringList(SL);		// Ошибки не будет. SL2 будет равно nil.
end;

Часто возникает необходимость определить потомком какого класса является объект. Для этого служит оператор is:

procedure FieldChange(Sender, Control: TObject; const FieldName: String);
begin
  if Control is TdxEdit then
  begin
    if TdxEdit(Control).ValidateText thenend
   else if Control is TCustomDBEditButton thenend;

Для упрощения доступа к элементам класса используется оператор with:

var
  SL: TStringList;
begin
  SL := TStringList.Create;
  with SL do
  begin
    Add('Один');
    Add('Два');
   end;end;

Создавать объект можно прямо в операторе with:

with TStringList.Create do
begin
  Add('Один');
  Add('Два');// Не забываем удалить объект
  Free;
end;