Помогите адаптировать паттерн Abstract Factory

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Помогите адаптировать паттерн Abstract Factory

Сообщение Climber » 08.12.2008 19:21:08

Код взят здесь: http://www.rsdn.ru/?article/patterns/Ab ... actory.xml, в readme файле написано, что код тестировался под Delphi 5.
Я брал из примера куски кода (там очень много лишнего, что к паттерну никак не относится) и понемногу добавлял в свой проект.
Принцип действия примера: в исходном коде проекта инициализируется только главный модуль (потомок TDataModule). Примерно так:
Код: Выделить всё
  Application.Initialize;
  Application.CreateForm(TMainDataModule, MainDataModule);
  Application.Run;

Этот кусок работает.
Вот кусок кода модуля "фабрика":
Код: Выделить всё
function TModuleFactory.CreateModule(ModuleType: TModuleType; Mediator: TMediator): TBaseModule;
var
  ModuleClass: TModuleClass;
begin
  { выбор класса для реализации }
  case ModuleType of
    MyDataModuleDescendant:
      ModuleClass :=TMyDataModuleDescendant;
   end;

  { реализация модуля по выбранному классу }
  Application.CreateForm(ModuleClass, Result);  // проблема начинается отсюда

  Result.Mediator := Mediator;
end;

Этот модуль вызывает фабрику, которая уже создает остальные модули и регистрирует их в медиаторе. Но почему-то при вызове Application.CreateForm(ModuleClass, Result); появляется ошибка "Project raised exception class 'EStreamError' with message: Failed to initialize component: No streaming method available.". Если в этом месте потомок DataModule заменить потомком формы, то все работает нормально - форма инициализируется и выводится на экран. Собственно вопрос: почему в одном месте программы DataModule инициализируется, а в другом - нет? Может, я что-то пропустил?
Более точно - проблема тут (application.inc):
Код: Выделить всё
procedure TApplication.CreateForm(InstanceClass: TComponentClass;
  out Reference);
var
  Instance: TComponent;
  ok: boolean;
  AForm: TForm;
begin
  // Allocate the instance, without calling the constructor
  Instance := TComponent(InstanceClass.NewInstance);
  // set the Reference before the constructor is called, so that
  // events and constructors can refer to it
  TComponent(Reference) := Instance;

  ok:=false;
  try
    if (FCreatingForm=nil) and (Instance is TForm) then
      FCreatingForm:=TForm(Instance);
    Instance.Create(Self); // Ошибка на этой строке
    ok:=true;
Climber
постоялец
 
Сообщения: 415
Зарегистрирован: 03.06.2007 20:09:57
Откуда: Москва

Re: Помогите адаптировать паттерн Abstract Factory

Сообщение Sergei I. Gorelkin » 08.12.2008 20:30:56

Как я понял, при инициализации приложения создается один датамодуль, а в фабрике - другой. Удается ли создать этот "другой" модуль обычным способом - прямым вызовом конструктора?
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1405
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Помогите адаптировать паттерн Abstract Factory

Сообщение Climber » 08.12.2008 23:30:17

Странно, почему-то никак не удается... Пробовал через Application.CreateForm и через TMyDataModuleDescendant.Create, ошибка всегда одна и та же. "Другой" модуль выглядит так:
Код: Выделить всё
type
  TBaseModule = class (TDataModule)
  private
    FMediator: TMediator;
  public
    procedure Execute(Command: TCommand); virtual; abstract;
    property Mediator: TMediator read FMediator write FMediator;
  end;

TMyDataModuleDescendant = class(TBaseModule)
   
  end;
Как в примере - один прямой потомок от TDataModule, а все остальные - потомки от него. Прямой наследник запускается, а через одного - нет. Может, я что-то про наследование не знаю, чего знать надо бы? Я про него вообще мало что знаю...
Climber
постоялец
 
Сообщения: 415
Зарегистрирован: 03.06.2007 20:09:57
Откуда: Москва

Re: Помогите адаптировать паттерн Abstract Factory

Сообщение Sergei I. Gorelkin » 09.12.2008 00:31:10

Тут, похоже, дело как-то связано с ресурсами (файлы *.lfm/*.lrs, аналоги дельфевых *.dfm). Сообщение об ошибке обусловлено невозможностью загрузки ресурса, а механизм их загрузки отличается от Дельфи и несколько более кучерявый, так сказать.

В Дельфи, если .dfm есть для TMyDataModuleDescendant, то для TBaseModule его может не быть - все равно загрузится. Как в Лазаре - не знаю, надо адресовать этот вопрос к людям, более близко с ним знакомым.

Если код копировать частями из дельфевого проекта, то оно едва ли заработает. Нужно либо весь проект целиком конвертировать с помощью соответствующей команды из меню, либо создавать датамодули средствами IDE, потом копировать код в полученную заготовку.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1405
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Помогите адаптировать паттерн Abstract Factory

Сообщение Climber » 09.12.2008 11:50:15

Создал lfm файлы (по аналогии с модулями, которые создает сам лазарус). И для TBaseModule, и для наследника. Все равно, та же ошибка... В крайнем случае, придется идти на "преступление" - обойтись без модулей и заменить их формами, смешивая интерфейс и реализацию :|

Добавлено спустя 15 минут 24 секунды:
Хм, а что я нашел тут:
1:
"This exception problem exists even if I just create a normal application and
do a

var M : TCustomHTTPModule;
begin
M:=TCustomHTTPModule.Create(Form1{Self});//<- crashing here
M.Free;
end;

for any TDataModule descendant in the above object chain. "Failed to initialize component: No streaming method available." exception is always triggered."
2 (ответ):
"It is correct. TDataModule is intended to be designed in an IDE, and to load itself from a stream. I assume you are not using Lazarus to create your descendents; if you were, they would be streamed correctly."
Ушел пробовать...

Добавлено спустя 2 часа 29 секунд:
Дело действительно было в ресурсах. Если создавать модуль через Файл-> Создать->DataModule, а наследников создавать через Файл-> Создать->Inherited Component, то все запускается. Единственное, при закрытии главной формы появляется сообщение "External: SIGSEGV" в этом месте:
Код: Выделить всё
procedure TApplication.Notification(AComponent : TComponent;
  Operation : TOperation);
begin
  if Operation = opRemove then begin
    FLastMouseControlValid:=false;
    if AComponent=FMouseControl then
      FMouseControl:=nil;
    if AComponent=FCreatingForm then
      FCreatingForm:=nil;
    if AComponent=FHintWindow then
      FHintWindow:=nil;
    if AComponent=FHintTimer then
      FHintTimer:=nil;
    if FComponentsToRelease<>nil then
      FComponentsToRelease.Remove(AComponent);
    if AComponent = MainForm then begin
      FMainForm:= nil;
      Terminate;
    end;
  end;
  inherited Notification(AComponent,Operation); // в этой строке
end;
Осталось дело за малым...

Добавлено спустя 5 часов 27 минут 8 секунд:
И последний вопрос. В медиаторе:
Код: Выделить всё
procedure TMediator.SendMessage(Command: TCommand);
var i: integer;
begin
  for i:=0 to ModuleList.Count-1 do
    TBaseModule(ModuleList[i]).Execute(Command);
end;

На строке TBaseModule(ModuleList[i]).Execute(Command) возникает ошибка RunError 211. Я вообще не очень понимаю, что в этой строке происходит? Пробовал заменить на (ModuleList[i] as TBaseModule).Execute(Command), но лазарус такое не принял (видимо, это не совсем то же самое)...
Climber
постоялец
 
Сообщения: 415
Зарегистрирован: 03.06.2007 20:09:57
Откуда: Москва


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 19

Рейтинг@Mail.ru