Можно ли изменить методы записи свойств программно, используя RTTI для создания объектно-ориентированных элементов управления? - PullRequest
4 голосов
/ 22 февраля 2012

У меня есть бизнес-объект, который я хотел бы лучше подключить к своему пользовательскому интерфейсу.Я видел несколько частичных решений для создания объектов с учетом данных, но все они включали значительные изменения в моем бизнес-объекте, включая дополнительный уровень абстракции.

Я искал улучшенный RTTI в новых версияхDelphi, и это выглядит очень интересно и полезно.Мне интересно, могу ли я использовать его для программного внедрения новых методов записи для всех свойств.

Способ, которым это будет работать, заключается в том, что мой потомок TEdit будет давать ссылку на свойство объекта при построении формы.Затем TEdit вставляет ссылку на себя в атрибут для этого свойства (и, конечно, удаляет себя в деструкторе или получает другую ссылку).TEdit также гарантирует, что метод записи для свойства будет заменен на метод, который уведомляет TEdit об изменениях после вызова исходного метода записи.

Возможно ли это?Большим ограничением показа было бы то, что внедрение нового метода записи невозможно, поэтому заголовок этого вопроса.

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

Ответы [ 3 ]

1 голос
/ 22 февраля 2012

Можно ли программно изменять методы записи свойств, используя RTTI для создания объектно-ориентированных элементов управления?

Нет, это невозможно. RTTI дает вам информацию, но не дает возможности изменять типы во время выполнения.

Большой ограничитель показа состоит в том, что внедрение нового метода записи невозможно, поэтому заголовок этого вопроса

Чтобы вы могли изменить это во время выполнения, должно быть что-то похожее на обработчик событий, который вы можете установить. Это простая концепция, но она имеет некоторые накладные расходы времени выполнения, как во время вызова (это было бы косвенным, когда обычно достаточно прямого вызова), так и с точки зрения требуемой памяти (каждое свойство потребовало бы дополнительного поля стиля TEvent). вы легко реализуете это, если вам это нужно, но было бы вредно, если бы компилятор автоматически генерировал такой код для всех классов «на всякий случай».

Если вы думаете о том, чтобы как-то исправить код в памяти во время выполнения, это не сработает и в лучшем случае будет ненадежным.

1 голос
/ 22 февраля 2012

Ваш вопрос уже ставит вас впереди меня с навыками программирования, поэтому я просто добавлю, как я мог бы подойти к этому:

Если бы я попытался написать что-то подобное, я бы, вероятно, начал с TListдля каждого поля в вашем TBusinessObject.Этот список будет использоваться для указания того, что необходимо обновить, когда вам нужно было вытолкнуть изменение.

Поэтому, когда TEdit создан, он добавит себя в список, который был связан с частью данных в вашемTBusinessObject.Когда TBusinessObject обновит этот фрагмент данных, он будет проходить по списку прикрепленных объектов.Он увидит TEdit и, зная, что это TEdit, запустит код для обновления .Text.Если бы я прикрепил TCaption, то код обновил бы .Caption.

TEdit, как вы указали, должен будет сообщить TBusinessObject, когда его значение было обновлено.Я полагаю, это сложная ситуация - вы можете создать новый TEdit и добавить в TList, чтобы поддерживать, кому он должен сообщать, когда он изменяется.Если вы использовали .Tag для указания номера поля в TBusinessObject, тогда OnChange (или любое другое событие) может затем вызвать что-то вроде TBusinessObject.FieldUpdate [TEdit.Tag, NewValue], что затем активирует вашу бизнес-логику.Это, в свою очередь, может заставить TBusinessObject обновлять другие поля, которые могут иметь свои собственные списки TList для полей для обновления.

Для предотвращения циклического обновления потребуется наличие способа обновления элемента управления без инициирования событий.Для одной программы, которую я написал, у меня было два метода для обновления элемента управления: SetValue и ChangeValue.SetValue отключил любые события (OnChange, OnValidate), обновил значение элемента управления и затем снова включил события.ChangeValue просто изменил значение и позволил любому из событий элемента управления запускаться по мере необходимости.

Возможно, есть более изящные способы сделать это, но, надеюсь, это даст вам пищу для размышлений.

0 голосов
/ 22 февраля 2012

В этом посте , озаглавленном Внедрение Великого Раскола , Кобус Крюгер рассказал о бизнес-объектах.

Решение, которое он приготовил, по существу соответствует требованиямс учетом ваших требований:

  1. Используйте расширенные функции RTTI, представленные в последней версии Delphi.
  2. Отделение бизнес-логики от логики представления.

ЛюбойPODO (Plain Old Delphi Object) будет действовать как бизнес-объект!

Волшебство заключается в классе TObjectBinding , который связывает любой TWinControl с любым бизнес-объектом.

Выдержка:

TObjectBinding = class
private
  fCtx: TRttiContext;
  fControlType: TRttiType;
  fObjType: TRttiType;

  fPropFieldMapping: TDictionary<TRttiProperty, TRttiField>; // Dictionary of object Properties & corresponding Fields

  fControl: TWinControl; // The control (normally form)
  fObj: TObject; // Object it represents.

  procedure CreateMappings; 

  function FindField(Prop: TRttiProperty; out Field: TRttiField): Boolean;
  function FieldClass(Field: TRttiField): TClass;

  // Modify these to change the rules about what should be matched.
  function IsValidField(Field: TRttiField): Boolean;
  function IsValidProp(Prop: TRttiProperty): Boolean;

  // Modify these to change the mappings of property type to VCL control class.
  procedure AssignField(Prop: TRttiProperty; Field: TRttiField);
  procedure AssignProp(Prop: TRttiProperty; Field: TRttiField);

  // Used from AssignField/AssignProp. Extend these to support a wider range of properties.
  function GetPropText(Prop: TRttiProperty): string;
  procedure SetPropText(Prop: TRttiProperty; const Text: string);
public
  constructor Create(Control: TWinControl; Obj: TObject);
  destructor Destroy; override;
  //
  procedure Load;
  procedure Save;
end;

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

...