Pull to refresh

Подробнее об атрибутах в Delphi 2010. Пример — проверка данных

Reading time 4 min
Views 6.6K
Original author: Malcolm Groves
МышаВ моей предыдущей статье Кратко об атрибутах в Delphi 2010, я показал основы связанные с созданием, применением и опросом атрибутов. Однако, я не привел пример, для чего вы могли бы их использовать.

Вероятно, наиболее распространенным является пример для персистентности, и, действительно, кто-то опубликовал подобный пример на сайте Wings of Wind. Я хотел бы показать другое их применение — для проверки данных (Validation).


Обратите внимание, то что будет показано ниже, это только пример использования атрибутов, а не полный фреймворк для проверки данных. Рассматривайте его, как испытание концепции, а не как готовый код.

Итак, с предупреждениями закончили, в таком случае я бы хотел иметь возможность добавить в мои классы метаданные указывающие правила проверки. Может быть, я хочу чтобы класс TPerson класс считался верным, когда Name — это непустое значение и значение Age лежит в диапазоне от 18 до 65. Один из способов добиться этого снабдить соответствующие свойства атрибутами, определяющими эти правила, и затем задействовать некоторый код, использующий RTTI для обработки любого переданного объекта и проверяющий каждый клик для полноразмерного изображения. Значения свойств соответствуют атрибутам присоединенным к ним.
Большая схема

Уловили? Отлично, теперь давайте посмотрим.

Я определил несколько атрибутов для проверки данных, чтобы показать как они работают с разными типами. Создана небольшая иерархия, которая делает код проверки чуть более общим. Я создал атрибуты только для строк и чисел, но вы поймете эту идею.

Взглянем на исходный код на одного из классов:

 MinimumIntegerAttribute = class(BaseIntegerValidationAttribute)
 private
  FMinimumValue: Integer;
 public
  constructor Create(MinimumValue : Integer; const FailureMessage : string);
  function Validate(Value : Integer) : boolean; override;
 end;



Конструктор содержит два параметра, метод проверки очень прост (в этом случае правило простое). Выглядит это так:

function MinimumIntegerAttribute.Validate(Value: Integer): boolean;
begin
 Result := Value >= FMinimumValue;
end;



Теперь я определил атрибуты и могу использовать их так:

TPerson = class
 private
  FName: String;
  FAge: Integer;
 public
  [NonEmptyString('Must provide a Name')]
  property Name : String read FName write FName;
  [MinimumInteger(18, 'Must be at least 18 years old')]
  [MaximumInteger(65, 'Must be no older than 65 years')]
  property Age : Integer read FAge write FAge;
 end;


Да, еще один TPerson. Не очень оригинально.

Как вы видите, я снабдил свойство Name атрибутом NonEmtpyString, и добавил сообщение об ошибке для неверного значения. Я также отметил свойство Age атрибутами minimum и maximum. Вероятно, более полезным является определение атрибута для диапазона, но я хотел показать применение к одному свойству двух атрибутов.

Для этого примера, я создал простую функцию, которая делает проверку:

function Validate(Target : TObject; ErrorList : TStrings) : boolean;
var
 ctx : TRttiContext;
 t : TRttiType;
 p : TRttiProperty;
 a : TCustomAttribute;
begin
 Result := True;

 if not Assigned(Target) then
  raise Exception.Create('Can''t validate nil object');

 if not Assigned(ErrorList) then
  raise Exception.Create('Can''t validate with a nil ErrorList');

 ctx := TRttiContext.Create;
 try
  t := ctx.GetType(Target.ClassType);
  for p in t.GetProperties do
   for a in p.GetAttributes do
    if a is BaseIntegerValidationAttribute then
    begin
     if not BaseIntegerValidationAttribute(a).Validate(p.GetValue(Target).AsInteger) then
      ErrorList.Add(BaseValidationAttribute(a).FailureMessage);
    end
    else if a is BaseStringValidationAttribute then
    begin
     if not BaseStringValidationAttribute(a).Validate(p.GetValue(Target).AsString) then
      ErrorList.Add(BaseValidationAttribute(a).FailureMessage);
    end
 finally
  ctx.Free;
 end;
end;


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

Далее, для каждого свойства, я выберу все атрибуты. В зависимости от типа атрибута, я вызову метода проверки, передавая значение для проверки. Если вызов Validate не удался, я добавляю FailureMessage в список ErrorList.

Безусловно, вы можете сделать лучше. Например, мне не нравится, что я должен обновлять метод Validate для каждого проверяемого типа и просто выводить ошибки в TStrings. Представлять список ValidationFailure объектов, вероятно, будет более правильно. Тем не менее, за 45 минут работы, я думаю это описывает идею достаточно хорошо. Вы можете скачать образец кода здесь.

Надеюсь это будет вам полезно, если вы захотите использовать атрибуты для персистентности, проверки данных или для чего-либо другого.
Несколько человек говорили, что они не видят смысла в атрибутах. Возможно, стоит изучить их и оставить на некоторое время, чтобы они успели «созреть» в вашей голове. Говорят, что наш язык формирует наши мысли, и это может быть просто потому, что Delphi в прошлом не поддерживал атрибуты, мы не привыкли к думать о них в поисках решения. Как и с дженериками, анонимными методами и интерфейсами до них, я нахожу, что чем больше я изучаю их, тем больше появляется идей их использования.

translated.by/you/more-attributes-in-delphi-2010/into-ru/trans
Перевод: © r3code, TDelphiBlog.
Tags:
Hubs:
+4
Comments 43
Comments Comments 43

Articles