Как написать собственный класс?

Материал из DRKB

Как написать собственный класс?[править | править код]

Вот пример написания класса. Этот класс вычисляет сумму квадратов введенных чисел. Этот класс написан мной только для примера, и я исходил из соображений наглядности, а не оптимальности. Большая часть реализации не только не оптимальна, но и бессмысленна, но показывает большую часть простейших приемов создания класса.

unit Unit2;

interface

Uses classes, Sysutils;

{ Нам нужен процедурный тип для создания собственного события. Собственно - это описание процедуры которая должна будет исполнятся при каких-нибудь обстоятельствах }
type
  TError = procedure(Sender: TObject; Error: string) of object;

{ Описание нашего класса, мы его наследуем от TObject, потому что нам практически не нужна
  никакая функциональность предков }
type
  TStatistic = class(TObject)
  private { здесь описываются только внутренние переменные и процедуры - "для служебного пользования" }
    { Описание полей, т.е. переменных которые работают только внутри класса, "снаружи" они не
      доступны. Имеют префикс "F" - Field }
    FList: TStringList;
    FPrecision: byte;
    { Тоже переменная - для определения события }
    FOnError: TError;

    { функция - будет использоваться только внутри класса, "снаружи" напрямую не доступна }
    function GetCount: Integer;

  public { Описанное здесь доступно для пользователя класса }
    { Конструктор - метод создания класса, имеет смысл его описывать только если он делает
      что-то специфическое - например нам надо будет создать переменную FList. В противном случае
      его описание можно опустить - будет работать конструктор родительского класса }
    constructor Create;
    { Деструктор - метод, вызываемый автоматически при освобождении класса.
      Обратите внимание на модификатор override - он необходим! }
    destructor Destroy; override;

    { Описание методов - собственно методы мало чем отличаются от обычных процедур и функций }
    procedure AddValue(Value: string);
    procedure Clear();
    function Solve(): Real;

    { Описание свойств. Обратите внимание само свойство не способно хранить никакую информацию, это
      только указатель на внутреннюю структуру. Например для хранения свойства Precision используется
      переменная FPrecision. А для чтения свойства Count используется функция GetCount() }
    property Precision: Byte read FPrecision write FPrecision;
    property Count: Integer read GetCount;

    { Описание событий. Это указатель на процедуру. Сам класс реализации этой процедуры
      не знает. Классу известно только заголовок процедуры, вы в коде программы будете писать реализацию
      процедуры, а класс только в нужный момент передаст ей управление, используя указатель OnError }
    property OnError: TError read FOnError write FOnError;
  end;

implementation

{ TStatistic }

constructor TStatistic.Create;
begin
  { В начале надо вызвать конструктор класса-родителя
    Рекомендуется это делать всегда, даже если в этом нет необходимости }
  inherited;
  FList := TStringList.Сreate(); { создаем структуры нашего класса }
end;

destructor TStatistic.Destroy;
begin
  FList.Free(); { Освобождаем структуры нашего класса }
  inherited; { в последнюю очередь ОБЯЗАТЕЛЬНО вызываем деструктор класса-родителя }
end;

procedure TStatistic.AddValue(Value: String);
begin
  FList.Add(Value); { Примерно так мы реализуем метод }
end;

procedure TStatistic.Clear;
begin
  FList.Clear;
end;

function TStatistic.GetCount: Integer;
begin
  Result := FList.Count + 1;
end;

function TStatistic.Solve: Real;
var 
  i: integer;
begin
  Result := 0;
  for i := 0 to FList.Count - 1 do
  begin
    try
      Result := Result + (Sqr(StrToFloat(FList[i])));
    except
     { интересная конструкция. "on e: Exception do" - мы "отлавливаем" ошибку как переменную "e".
       Эта переменная имеет очень полезное свойство e.message - оно содержит описание ошибки. Далее
       следует вызов события. В начале мы проверяем использует ли пользователь событие:
       "if Assigned(FOnError) then", если использует то вызываем его процедуру: FOnError, с параметрами:
       self - зарезервированная переменная - указатель на экземпляр нашего класса, e.Message - описание
       ошибки }
      on e: Exception do 
        if Assigned(FOnError) then FOnError(Self, e.Message);
    end;
  end;
end;

end.

Вот пример использования этого класса:

unit Unit1;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    procedure OnError(Sender:TObject; Error: string);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  Statistic: TStatistic;
begin
  Statistic := TStatistic.Create();
  { Если создание объекта расположено рядом с освобождением, то рекомендуется "оборачивать"
    использование объекта в try ... finally. Если при использовании возникнет ошибка,
    и выполнение программы прервется, то блок finally, который выполняется в любом случае,
    позволит гарантированно освободить объект и избежать утечки памяти. }
  try
    Statistic.OnError := OnError;
    Statistic.AddValue('123423');
    Statistic.AddValue('123423');
    ShowMessage(FloaTtoStr(Statistic.Solve));
    Statistic.Clear();
    Statistic.AddValue('123423');
    Statistic.AddValue('12ssss3');
    ShowMessage(FloatToStr(Statistic.Solve));
  finally
    Statistic.Free();
  end;
end;

procedure TForm1.OnError(Sender: TObject; Error: string);
begin
  ShowMessage('Error inside class:' + Sender.ClassName + #13#10 + Error);
end;

end.


Author: Vit (www.delphist.com, www.drkb.ru, www.unihighlighter.com, www.nevzorov.org)
Source: http://forum.vingrad.ru
ID: 00092