Инспектор объектов и метаданные

Используя терминологию паттернов проектирования можно заметить, что менеджер объектов является фасадом, который сводит к минимуму зависимость подсистем инспектора друг от друга и контролирует обмен информации между ними. Далее будет описана только одна реализация менеджера. Конкретика этого менеджера состоит в том, что он использует те метаданные, которые формируются на основе метаклассов, то есть, поддерживает описанный выше способ организации метаданных. Как уже было сказано, можно было бы построить целое семейство различных менеджеров, но в данной версии инспектора я ограничился только одним менеджером.

TGsvObjectInspectorObjectInfo = class
 public
  constructor Create;
  destructor Destroy; override;
  function ObjectName: String; virtual;
  function ObjectTypeName: String; virtual;
  function ObjectHelp: Integer; virtual;
  function ObjectHint: String; virtual;
  function PropertyInfo(Index: Integer): PGsvObjectInspectorPropertyInfo;
  procedure FillList(Info: PGsvObjectInspectorPropertyInfo;
  List: TStrings); virtual;
  procedure ShowDialog(Inspector: TComponent;
  Info: PGsvObjectInspectorPropertyInfo;
  const EditRect: TRect); virtual;
  function GetStringValue(Info: PGsvObjectInspectorPropertyInfo):
  String; virtual;
  procedure SetStringValue(Info: PGsvObjectInspectorPropertyInfo;
  const Value: String); virtual;
  function GetIntegerValue(Info: PGsvObjectInspectorPropertyInfo):
  LongInt; virtual;
  procedure SetIntegerValue(Info: PGsvObjectInspectorPropertyInfo;
  const Value: LongInt); virtual;
  property TheObject: TObject read GetObject write SetObject;
 end;

Можно заметить, что методы менеджера напоминают методы базового класса метаданных TGsvObjectInspectorTypeInfo. И это не случайно, ведь в большинстве случаев менеджер просто перенаправляет запрос соответствующему методу конкретного класса метаданных, то есть, играет роль диспетчера.
Метод PropertyInfo напоминает метод ChildrenInfo метакласса - для каждого значения индекса функция возвращает указатель на метаданные свойства, а при завершении итерации по всем свойствам она возвращает nil. Наиболее существенное отличие от ChildrenInfo состоит в том, что PropertyInfo рекурсивно обходит все вложенные свойства и дополняет структуру TGsvObjectInspectorPropertyInfo несколькими динамически формируемыми полями. Здесь уместно упомянуть, что при описании записи TGsvObjectInspectorPropertyInfo мы опустили несколько полей, которые были неважны с точки зрения метаданных. Вот эти поля:
HasChildren: Boolean;
Level: Integer;
Expanded: Boolean;
TheObject: TObject;
NestedObject: TObject;·HasChildren - указывает на наличие у данного свойства вложенных подсвойств, ·Level - уровень свойства в полном дереве свойств, ·Expanded - признак того, что вложенные свойства раскрыты и отображаются, ·TheObject - объект или заместитель, которому принадлежит свойство, ·NestedObject - объект или заместитель вложенного свойства.
Первые три поля используются только визуальным компонентом инспектора, а последние два поля - менеджером и метаклассами. Для доступа к метаданным менеджер обращается к реестру метаданных, используя при поиске имя типа инспектируемого объекта. Кроме того, менеджер обращается к реестру при рекурсивном обходе вложенных свойств. Назначение остальных методов: ·FillList - перенаправляет запрос на заполнение списка перечислимых значений свойства конкретному метаклассу вложенного свойства, ·ShowDialog - перенаправляет запрос на отображение диалога-мастера конкретному метаклассу вложенного свойства, ·GetStringValue - получает значение свойства инспектируемого объекта в строковом виде на основе RTTI. Если свойство имеет вложенный метакласс, то используется его специализация (запрос перенаправляется метаклассу), а иначе выполняется стандартное преобразование, например, из типа Double в тип String, ·SetStringValue - устанавливает значение свойства на основе заданного строкового значения, ·GetIntegerValue и SetIntegerValue - подобны двум предыдущим методам, но специализированы не на строковом, а на целочисленном значении свойства.
Говоря о перенаправлении запросов от менеджера, нельзя не упомянуть о тех методах метаклассов, которых мы только коснулись в первом разделе статьи. В текущей версии инспектора определено несколько вспомогательных специализированных классов, порожденных от базового класса TGsvObjectInspectorTypeInfo. Это: ·TGsvObjectInspectorTypeListInfo - предоставляет дополнительную функциональность при работе со свойствами, реализующими перечислимые типы. Такие свойства отображаются в инспекторе как выпадающие списки, ·TGsvObjectInspectorTypeSetInfo - помогает описывать свойства-множества, ·TGsvObjectInspectorTypeFontInfo - специализируется на описании свойства типа TFont и инкапсулирует стандартный Windows-диалог выбора шрифта, ·TGsvObjectInspectorTypeColorRGBInfo - специализируется на описании простого свойства типа TColor и инкапсулирует стандартный Windows-диалог выбора цвета.
Все эти классы являются вспомогательными и уменьшают трудозатраты на описание конкретных классов метаданных. Для примера рассмотрим подробнее парочку из указанных вспомогательных классов.

type

 TGsvObjectInspectorListItem = record

  Name: String; // имя элемента списка

  Data: LongInt; // значение элемента списка

 end;

 PGsvObjectInspectorListItem = ^TGsvObjectInspectorListItem;

 TGsvObjectInspectorTypeListInfo = class(TGsvObjectInspectorTypeInfo)

 protected

  class function ListEnumItems(Index: Integer):

  PGsvObjectInspectorListItem; virtual;

 public

  class procedure FillList(AObject: TObject; List: TStrings); override;

  class function IntegerToString(const Value: LongInt):

  String; override;

  class function StringToInteger(const Value: String):

  LongInt; override;

 end;

 class function TGsvObjectInspectorTypeListInfo.ListEnumItems(

  Index: Integer): PGsvObjectInspectorListItem;

 begin

  Result := nil;

 end;

 class procedure TGsvObjectInspectorTypeListInfo.FillList(AObject: TObject;

  List: TStrings);

 var

  i: Integer;

  p: PGsvObjectInspectorListItem;

 begin

  i := 0;

  p := ListEnumItems(0);

  while Assigned(p) do begin

  List.AddObject(p^.Name, TObject(p^.Data));

  Inc(i);

  p := ListEnumItems(i);

  end;

 end;

 class function TGsvObjectInspectorTypeListInfo.IntegerToString(

  const Value: Integer): String;

 var

  i: Integer;

  p: PGsvObjectInspectorListItem;

 begin

  Result := '';

  i := 0;

  p := ListEnumItems(0);

  while Assigned(p) do begin

  if p^.Data = Value then begin

  Result := p^.Name;

  Break;

  end;

  Inc(i);

  p := ListEnumItems(i);

  end;

 end;

 class function TGsvObjectInspectorTypeListInfo.StringToInteger(

  const Value: String): LongInt;

 var

  i: Integer;

  p: PGsvObjectInspectorListItem;

 begin

  Result := 0;

  i := 0;

  p := ListEnumItems(0);

  while Assigned(p) do begin

  if p^.Name = Value then begin

  Result := p^.Data;

  Break;

  end;

  Inc(i);

  p := ListEnumItems(i);

  end;

 end;

Отправить комментарий

Проверка
Антиспам проверка
Image CAPTCHA
...