Тестируем созданный класс TPASSMomentum (Delphi 7: отладчик, create, private, protected, breakpoint, точка останова)
статья экспортирована с сайта http://easyprog.ru
Сегодня мы приступим к тестированию созданного нами класса индикатора. Для этого создадим тестовый пример. Сначала разместим на форме меню mmMenu и сделаем в нем всего лишь один пункт: "Файл"-->"Загрузить" (itLoad). Затем поместим диалог выбора файла: odOpenDialog, метки lbDateTime и lbResult, кнопкочку "Вычислить" (btnCalk), два компонента SpinEdit (seCandle и seDT), их можно найти на закладке Samples. Вот так примерно будет выглядеть наша форма*:
В фильтре диалога выбора файлов установим фильтр текстовые файлы (с расширенеим *.txt)
Добавим в класс основной формы окна переменные FPriceSource и FIndicator (добавленное выделено красным шрифтом):
TfrmMomentum = class(TForm) odOpenDialog: TOpenDialog; mmMenu: TMainMenu; itFile: TMenuItem; itLoad: TMenuItem; lbResult: TLabel; btnCalk: TButton; seDT: TSpinEdit; lbDateTime: TLabel; seCandle: TSpinEdit; procedure itLoadClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure btnCalkClick(Sender: TObject); procedure seCandleChange(Sender: TObject); private { Private declarations } FPriceSource:TPASSPriceSource; FIndicator:TPASSMomentum; public { Public declarations } end; |
в обработчике события OnCreate формы нам нужно полям FPriceSource и FIndicator присвоить nil:
procedure TfrmMomentum.FormCreate(Sender: TObject); begin FPriceSource:=nil; FIndicator:=nil; end; |
procedure TfrmMomentum.itLoadClick(Sender: TObject);
begin
if odOpenDialog.Execute then
begin
if FPriceSource<>nil then FreeAndNil(FPriceSource);
FPriceSource:=TPASSPriceSource.Create('',false);
FPriceSource.LoadDataFromTextFile(odOpenDialog.FileName);
end;
end; |
procedure TfrmMomentum.seCandleChange(Sender: TObject);
begin
FPriceSource.CurrentItemIndex:=seCandle.Value;
lbDateTime.Caption:= DateTimeToStr(FPriceSource.GetDataByFieldName('DateTime'));
end; |
procedure TfrmMomentum.btnCalkClick(Sender: TObject); begin if FIndicator<>nil then FreeAndNil(FIndicator); FIndicator:=TPASSMomentum.Create(seDT.Value,'Close'); FIndicator.PriceSource:=FPriceSource; lbResult.Caption:= intToStr(FIndicator.GetParameterByName('Value')); end; |
Все, запускаем программу. загружаем индикатор и жмем на кнопку "Вычислить".
Опаньки! У нас вылезло сообщение об ошибке*.
Данная ошибка возникла при попытке выполнить операцию
FParameters.Add('PriceFieldType',APriceFieldType)*;
Давайте при помощи отладчика узнаем, что содержалось в FParameters на момент ошибки. и так, ставим точку останова, просто щелкнув мышкой слева от строки*:
Точку останова можно так же поставить (или снять), нажав на клавиатуре клавишу F5, либо через всплывающее меню (по щелчку правой кнопки мыши) "Debuп" --> "Toggle breakpoint"*.
Теперь запустим программу. Когда мы нажмем на кнопочку "Вычислить" у нас выполнения программы прервется в точке останова. что бы посмотреть значения переменных, откроем окно "Watches" через меню "View" --> "Debug Windows" --> "Watches"*:
В это окно добавим переменную FParameters, это можно сделать нажав на клавиатуре клавишу Insert. При этом должно открыться окно добавления переменной*:
Как видим FParameters у нас равно nil*:
Иными словами, данная переменная у нас почему то не инициализировалась, хотя мы вызвали конструктор родительского класса:
inherited Create; |
inherited Create;
Когда программа у остановится, начнем выполнять ее пошагово, нажимая на клавишу F7 на клавиатуре. У нас зайдет в конструктор класса TPASSIndicator, затем в выполниться :
inherited Create
а после него
FParameters:=TPASSParameters.Create
Как видим, у нас FParameters инициализируется. Но, выйдя снова в конструктор класса TPASSMomentum он принимает значение nil.
Оказывается, все дело в особенностях Delphi. Дело в том, что одна и та же переменная, объявленная в разных секция, на самом деле разная. Иными словами, мы получили две локальных переменных. А у нас как раз в классе TPASSIndicator переменная FParameters объявлена как private, а в классе TPASSMomentum как protected. Мы хотели таким образом добраться до приватной переменной. А не тут то было!
Если бы мы по какой то причине не могли править модуль PASSIndicators (например, не имели бы исходников), то добраться до FParameters бло бы очень трудно. Таким образом, на примере данной ошибки ясно и понятно показано, в каких случаях нужно объявлять переменные в секции private, и в каких случаях protected. И так, прорезюмирую:
- Если переменная не должна быть видимой снаружи класса, потому что при записи в нее значения требуется производить какие то действия, то данную переменную следует объявить как private или protected, а для доступа к переменной организовать свойства, к которому подвязать методы для записи в переменную. Смотрите как объявлен класс PASSIndicators:
TPASSIndicator=Class(TPASSAbstractDataSource) private FParameters:TPASSParameters; // значения параметров индикатора. FPriceSource:TPASSPriceSource; // источник котировок. procedure SetPriceSource(APriceSource:TPassPriceSource); procedure UpdateParameters; virtual; abstract; public constructor Create; property PriceSource:TPASSPriceSource read FPriceSource write SetPriceSource; procedure SetParameterByName(AParameterName:string; AParameterValue:Variant); virtual; function GetParameterByName(AParameterName:string):Variant; virtual; function GetResultByFieldName(AFieldName:string):double; virtual; abstract; function GetResultByFieldNameAndIndex(FieldName:string; Index:LongInt):double; virtual; abstract; function GetResultByFieldNum(AFieldName:integer):double; virtual; abstract; function GetResultByFieldNumAndIndex(FieldName:Integer; Index:LongInt):double; virtual; abstract; procedure First; virtual; abstract; procedure Last; virtual; abstract; function Next:boolean; virtual; abstract; function Prev:boolean; virtual; abstract; function GetIndicatorName:string; virtual; abstract; Destructor Destroy; virtual; end; |
- Если необходимо, что бы можно было поиметь доступ к переменной в дочерних классах, которые могу быть объявлены в других модулях (как в нашем случае например), то объявлять данную переменную класса нужно в секции именно protected. Если есть причниа скрыть эту переменную и сделать невидиму в дочерних классах то используем private.
И так мы выяснили, что нам нужно внутренние переменные класса TPASSIndicator объявить как protected. У нас есть исходники мы можем это сделать. Но переписывать модуль PASSIndicators, а заодно и проводить рефреминг кода мы будем на следующем уроке.
Скриншоты, помеченные знаком * , являются цитатами и иллюстрациями в соответствии со ст. 1274 ГК РФ программного продукта "Delphi", авторское право на который принадлежит "Borland Software Corporation".
Рекомендуем Вам посетить сайт источник: http://easyprog.ru