Software Development Kit
to the Delphi-Win32 and Free Pascal compilers
Home > Wiki > br/ProjetoAgenda/Artigo2

br/ProjetoAgenda/Artigo2

english (en) | português (pt)



Neste artigo será feita a implementação da parte lógica do projeto.

Contents

Objetos de negócio

As classes de negócio da aplicação serão declaradas juntas, separadas por grupo. Cada subpasta dentro da pasta Core terá apenas uma unit com todas as classes de negócio daquele grupo.

CustomBO

Crie a unit (sem form) {agenda}\Source\Core\Custom\CustomBO.pas e coloque o seguinte conteúdo:

unit CustomBO;

interface

uses
  PressSubject, PressAttributes;

type
  TCustomObject = class(TPressObject)
  end;

  TCustomQuery = class(TPressQuery)
  end;

  TCustomParts = class(TPressParts)
  end;

  TCustomReferences = class(TPressReferences)
  end;

implementation

initialization
  TCustomObject.RegisterClass;
  TCustomQuery.RegisterClass;
  TCustomParts.RegisterAttribute;
  TCustomReferences.RegisterAttribute;

finalization
  TCustomObject.UnregisterClass;
  TCustomQuery.UnregisterClass;
  TCustomParts.UnregisterAttribute;
  TCustomReferences.UnregisterAttribute;

end.

O objetivo destas classes é figurar entre as classes do PressObjects e as classes de negócio da aplicação, para que seja possível incluir ou alterar funcionalidades de forma global e sem precisar alterar cada classe.

ContatoBO

Crie outra unit (também sem form) {agenda}\Source\Core\Contato\ContatoBO.pas e coloque o seguinte conteúdo:

unit ContatoBO;

interface

uses
  PressSubject, PressAttributes, CustomBO;

type
  {$M+}  // necessário no FPC
  TContatoFoneParts = class;
  {$M-}  // necessário no FPC

  TContato = class(TCustomObject)
    _Nome: TPressAnsiString;
    _Endereco: TPressAnsiString;
    _Fones: TContatoFoneParts;
    _Cidade: TPressReference;
  protected
    class function InternalMetadataStr: string; override;
  end;

  TContatoQuery = class(TCustomQuery)
    _Nome: TPressAnsiString;
    _Cidade: TPressReference;
  protected
    class function InternalMetadataStr: string; override;
  end;

  TTipoFone = (tfFixo, tfCelular, tfFax);

  TContatoFone = class(TCustomObject)
    _TipoFone: TPressEnum;
    _Numero: TPressPlainString;
  protected
    class function InternalMetadataStr: string; override;
  end;

  TContatoFoneParts = class(TCustomParts)
  public
    class function ValidObjectClass: TPressObjectClass; override;
  end;

  TCidade = class(TCustomObject)
    _Nome: TPressAnsiString;
    _UF: TPressAnsiString;
  protected
    class function InternalMetadataStr: string; override;
  end;

  TCidadeQuery = class(TCustomQuery)
    _Nome: TPressAnsiString;
  protected
    class function InternalMetadataStr: string; override;
  end;

implementation

initialization
  PressModel.RegisterEnumMetadata(TypeInfo(TTipoFone), 'TTipoFone',
   ['Fone fixo', 'Celular', 'Fax']);

  TContato.RegisterClass;
  TContatoQuery.RegisterClass;
  TContatoFone.RegisterClass;
  TContatoFoneParts.RegisterAttribute;
  TCidade.RegisterClass;
  TCidadeQuery.RegisterClass;

finalization
  TContato.UnregisterClass;
  TContatoQuery.UnregisterClass;
  TContatoFone.UnregisterClass;
  TContatoFoneParts.UnregisterAttribute;
  TCidade.UnregisterClass;
  TCidadeQuery.UnregisterClass;

end.

Para implementar o metadata das classes de negócio, posicione o cursor de texto dentro da declaração da classe e pressione Shift + Ctrl + C. No método criado pela IDE, digite o seguinte bloco para a classe TContato:

class function TContato.InternalMetadataStr: string;
begin
  Result := 'TContato IsPersistent (' +
   'Nome: AnsiString(60);' +
   'Endereco: AnsiString(80);' +
   'Fones: TContatoFoneParts;' +
   'Cidade: Reference(TCidade);' +
   ')';
end;

Esta é a parte mais importante da declaração da classe. O PressObjects irá interpretar esta string e criar os atributos com base nestas informações. Os membros de classe iniciados com "_" receberão o endereço destes atributos assim que o objeto for instanciado.

Agora basta fazer o mesmo para as demais classes:

begin
  Result := 'TContatoQuery(TContato) (' +
   'Nome: AnsiString(60);' +
   'Cidade: Reference(TCidade);' +
   ')';
end;
begin
  Result := 'TContatoFone IsPersistent (' +
   'TipoFone: Enum(TTipoFone);' +
   'Numero: PlainString(15);' +
   ')';
end;
class function TContatoFoneParts.ValidObjectClass: TPressObjectClass;
begin
  Result := TContatoFone;
end;
begin
  Result := 'TCidade IsPersistent (' +
   'Nome: AnsiString(60);' +
   'UF: AnsiString(2);' +
   ')';
end;
begin
  Result := 'TCidadeQuery(TCidade) (' +
   'Nome: AnsiString(60);' +
   ')';
end;

MVP

As classes MVP também serão agrupadas, cada subpasta dentro da pasta Core terá apenas uma unit com todas as suas classes MVP.

MainMVP

Crie a unit (sem form) {agenda}\Source\Core\Main\MainMVP.pas e coloque o seguinte conteúdo:

unit MainMVP;

interface

uses
  PressMVPPresenter;

type
  TMainFormPresenter = class(TPressMVPMainFormPresenter)
  protected
    procedure InitPresenter; override;
  end;

implementation

end.

Utilizando o mesmo artifício da unit ContatoBO, posicione o cursor de texto em frente a declaração da classe TMainFormPresenter e pressione Shift + Ctrl + C. Dentro da declaração InitPresenter, implemente:

procedure TMainFormPresenter.InitPresenter;
begin
  inherited;
  BindPresenter(TContatoEditPresenter, 'NovoContatoButton');
  BindPresenter(TContatoQueryPresenter, 'PesqContatoButton');
  BindPresenter(TCidadeQueryPresenter, 'PesqCidadeButton');
  BindCommand(TPressMVPCloseApplicationCommand, 'FecharButton');
  PressUserData.Logon('', '');
end;

BindPresenter é um método que faz a ligação entre uma classe presenter e um componente, e o método BindCommand faz o mesmo com um command. Quando o evento OnClick destes componentes são acionados, o Presenter é executado e apresenta um form, ou o command é disparado.

O método Logon cria um usuário com privilégio de uso de todos os commands, e será substituido logo que o controle de acesso for implementado.

Estas declarações exigem algumas units:

implementation

uses
  PressUser, PressMVPCommand, ContatoMVP;

Este Presenter deve ser executado na inicialização da aplicação, é ele quem iniciará alguns serviços internos do PressObjects. Abra o arquivo principal do projeto - Project | View Source (Delphi e Lazarus) - e inclua a chamada a este presenter:

begin
  Application.Initialize;
  Application.CreateForm(TMainForm, MainForm);
  TMainFormPresenter.Run;
end.

A declaração TMainFormPresenter.Run ficará no lugar do tradicional Application.Run. Salve todos os arquivos do projeto.

CustomMVP

Crie a unit (sem form) {agenda}\Source\Core\Custom\CustomMVP.pas e coloque o seguinte conteúdo:

unit CustomMVP;

interface

uses
  PressMVPPresenter;

type
  TCustomEditPresenter = class(TPressMVPFormPresenter)
  protected
    procedure InitPresenter; override;
  end;

  TCustomQueryPresenter = class(TPressMVPQueryPresenter)
  protected
    procedure InitPresenter; override;
  end;

implementation

initialization
  TCustomEditPresenter.RegisterBO(TCustomObject);
  TCustomQueryPresenter.RegisterBO(TCustomQuery);

end.

Utilizando o mesmo artifício das units anteriores, posicione o cursor de texto em frente a declaração da classe TCustomEditPresenter e pressione Shift + Ctrl + C. Dentro da declaração InitPresenter, implemente:

procedure TCustomEditPresenter.InitPresenter;
begin
  inherited;
  BindCommand(TPressMVPSaveObjectCommand, 'GravarButton');
  BindCommand(TPressMVPCancelConfirmObjectCommand, 'CancelarButton');
end;

Estas instruções ligam um determinado command a um componente do form. Ao pressionar o botão, o command é executado e irá, respectivamente, gravar o objeto no banco e fechar o form, ou reverter as alterações feitas no objeto em memória e fechar o form.

Próxima classe, TCustomQueryPresenter. Posicione o cursor, pressione Shift + Ctrl + C e implemente:

procedure TCustomQueryPresenter.InitPresenter;
begin
  inherited;
  BindCommand(TPressMVPExecuteQueryCommand, 'PesquisarButton');
  CreateQueryItemsPresenter('QueryStringGrid');
end;

A primeira instrução liga um command padrão do Press ao botão de pesquisa. A segunda é uma instrução especial de Presenters de pesquisa, e indica ao framework MVP qual é o componente que irá apresentar o resultado da consulta.

Estas implementações dependem de declarações feitas em outras units. A cláusula uses da área implementation deve ficar assim:

uses
  PressMVPCommand, CustomBO;

ContatoMVP

Crie a unit (sem form) {agenda}\Source\Core\Contato\ContatoMVP.pas e coloque o seguinte conteúdo:

unit ContatoMVP;

interface

uses
  CustomMVP;

type
  TContatoEditPresenter = class(TCustomEditPresenter)
  protected
    procedure InitPresenter; override;
  end;

  TContatoQueryPresenter = class(TCustomQueryPresenter)
  protected
    procedure InitPresenter; override;
    function InternalQueryItemsDisplayNames: string; override;
  end;

  TContatoFoneEditPresenter = class(TCustomEditPresenter)
  protected
    procedure InitPresenter; override;
  end;

  TCidadeEditPresenter = class(TCustomEditPresenter)
  protected
    procedure InitPresenter; override;
  end;

  TCidadeQueryPresenter = class(TCustomQueryPresenter)
  protected
    procedure InitPresenter; override;
    function InternalQueryItemsDisplayNames: string; override;
  end;

implementation

uses
  ContatoBO;

initialization
  TContatoEditPresenter.RegisterBO(TContato);
  TContatoQueryPresenter.RegisterBO(TContatoQuery);
  TContatoFoneEditPresenter.RegisterBO(TContatoFone);
  TCidadeEditPresenter.RegisterBO(TCidade);
  TCidadeQueryPresenter.RegisterBO(TCidadeQuery);

end.

Agora falta o mapeamento entre as classes de negócio e os formulários. Com ajuda do Shift + Ctrl + C, implemente os seguintes códigos:

procedure TContatoEditPresenter.InitPresenter;
begin
  inherited;
  CreateSubPresenter('Nome', 'NomeEdit');
  CreateSubPresenter('Endereco', 'EnderecoEdit');
  CreateSubPresenter('Fones', 'FonesStringGrid', 'TipoFone;Numero');
  CreateSubPresenter('Cidade', 'CidadeComboBox', 'Nome');
end;

O método CreateSubPresenter cria um presenter e respectivos views e models. O parâmetro indica respectivamente o nome do atributo na classe de negócio e o nome do componente no form. O terceiro parâmetro, usado em composições e agregações, indica qual ou quais atributos da outra classe devem ser apresentados no controle.

procedure TContatoQueryPresenter.InitPresenter;
begin
  inherited;
  CreateSubPresenter('Nome', 'NomeEdit');
  CreateSubPresenter('Cidade', 'CidadeComboBox', 'Nome');
end;
function TContatoQueryPresenter.InternalQueryItemsDisplayNames: string;
begin
  Result := 'Nome;Cidade.Nome';
end;
procedure TContatoFoneEditPresenter.InitPresenter;
begin
  inherited;
  CreateSubPresenter('TipoFone', 'TipoFoneComboBox');
  CreateSubPresenter('Numero', 'NumeroEdit');
end;
procedure TCidadeEditPresenter.InitPresenter;
begin
  inherited;
  CreateSubPresenter('Nome', 'NomeEdit');
  CreateSubPresenter('UF', 'UFEdit');
end;
procedure TCidadeQueryPresenter.InitPresenter;
begin
  inherited;
  CreateSubPresenter('Nome', 'NomeEdit');
end;
function TCidadeQueryPresenter.InternalQueryItemsDisplayNames: string;
begin
  Result := 'Nome;UF';
end;

Testando o projeto

Dê um Save All e um Build All no projeto para corrigir os erros de compilação. Execute o projeto, pressione Shift + Alt + 9 para gerar o metadata do banco de dados e abra todos os forms para testar os bindings, mas não tente gravar nada ainda.

Obs.: forms e controles possuem menu de contexto (popup) acionados com o botão direito. Neste menu estão os atalhos usados para incluir, alterar, apagar, etc. as informações daquele componente.

Se tudo correr bem, siga para o próximo artigo. Em caso de problema revise as implementações, nomes de forms e units, e os parâmetros dos bindings conforme a mensagem de erro apresentada.