Análise e Desenho Orientados por Objetos (3/4)

By: Adail Retamal

Abstract: Parte 3 de 4. O Componente Genérico de Modelo. Revisando o modelo anterior. Descobrindo os métodos das classes. Persistência dos objetos. OCL (Object Constraint Language).

Agora que já sabemos sobre os quatro arquétipos e os identificamos em nosso modelo de classes para nosso sistema gerenciador de estacionamentos, vamos aprofundar nosso estudo um nível a mais e descobrir que podemos generalizar alguns conceitos para aplicar em qualquer tipo de modelagem de domínio.

Peter Coad, depois de construir milhares de modelos, nas mais diversas áreas, observou que as classes, já mapeadas de acordo com os arquétipos, seguiam um padrão de relacionamento entre si. Assim ele propôs um esquema genérico de associação entre os arquétipos, denominado DNC - Domain Neutral Component, que por falta de um equivalente em português chamarei de CGM - Componente Genérico de Modelo.

Hide image
Click to see full-sized image

Figura 1. O Componente Genérico de Modelo

O que esse esquema nos diz? Cada Momento-Intervalo (evento, serviço ou atividade, em rosa) pode possuir detalhes ou partes, os MI-Detalhes (também na cor rosa), que por sua vez são pequenos serviços ou eventos associados ao evento maior. Por exemplo, uma venda de supermercado (um evento maior) é composta por diversos itens de venda (os produtos que são comprados). Por isso existe a indicação de agregação entre o MI e o MI-Detalhe.

Os MI’s podem existir isoladamente ou participar de uma cadeia de eventos (manutenção de histórico de alterações, por exemplo). Daí a sugestão do auto-relacionamento no MI, com a indicação de qual é o objeto atual e dos planejados/possíveis.

Participando de um MI temos uma Pessoa, Lugar ou Coisa (PLC, em verde). Em geral temos vários objetos PLC’s participando simultaneamente de um evento, como é o caso no nosso modelo do estacionamento, onde numa Estada participam um Funcionário (Pessoa), um Veículo (Coisa) e um Estacionamento (Lugar). A PLC pode participar diretamente ou através de um Papel (em amarelo), conforme mostra a Figura 1. A função do arquétipo Papel é abstrair a PLC original, possibilitando, inclusive, a participação de PLC’s diferentes num mesmo papel. Por exemplo, o cliente (dono do veículo) pode ser uma pessoa física ou jurídica. Na hora de estacionar isso não importa, pois estaríamos mapeando o cliente (que é um papel) como participante do evento.

Por sua vez, a PLC pode conter certas informações que são comuns a vários objetos, algo semelhante a um catálogo ou lista de referência. Por exemplo, um veículo possui características como fabricante e modelo, as quais são iguais a muitos objetos. Podemos agrupar essas informações em uma classe Descrição (em azul), e associá-la à PLC.

Resumindo, o Componente Genérico de Modelo diz que num evento (Momento-Intervalo) participam Pessoas, Lugares ou Coisas, geralmente representando Papéis, sendo que as PLC’s podem referenciar Descrições comuns.

Se olharmos agora com mais cuidado o nosso modelo de classes apresentado na parte 2 deste artigo notaremos esse padrão! E quando você for criar seus próximos modelos, certamente observará essa tendência vez após vez. Outra forma de utilização do CGM é procurar no seu modelo por esse padrão e com isso descobrir classes, atributos e métodos que possam estar faltando. O CGM nos fornece mais previsibilidade ao processo de modelagem, tornando-o um pouco mais científico do que artístico.

    Revisando o Modelo

Como lição de casa do artigo anterior propus que o leitor fizesse uma análise do modelo proposto e verificasse se ele pode responder a perguntas importantes para o negócio. Munidos com as novas informações, creio que temos mais condições de criticar o modelo e melhorá-lo.

Por exemplo, na discussão sobre o dono do veículo acima vimos que pode ser uma pessoa física ou jurídica (empresa). Mas isso não está no nosso modelo original. Só temos uma classe Pessoa, que está sendo utilizada no contexto de pessoa física, pois o Funcionário relaciona-se com ela. Proponho, então, que transformemos a classe Pessoa em abstrata e derivemos dela as classes PessoaFisica e PessoaJuridica. O Funcionário tem que ser uma pessoa física, então relacionar-se-á com a classe PessoaFisica. Já o Cliente (provavelmente o dono do veículo), pode ser uma pessoa física ou jurídica. E agora, com qual classe faremos o relacionamento? Com a classe Pessoa, claro, para aproveitarmos o polimorfismo!

Sabendo que uma PLC pode ter uma Descrição, nossa classe Veículo pode ser complementada pela classe TipoVeiculo, que conterá a indicação do fabricante e do modelo. Isso permitirá, por exemplo, que o funcionário já escolha o tipo de carro a partir de uma lista previamente construída. Além do mais, se quisermos fazer estatísticas posteriormente usando essas informações será uma moleza!

Com essas alterações, nosso modelo de classes agora ficou como mostra a Figura 2.

Hide image
Click to see full-sized image

Figura 2. Nosso modelo revisado

    Descobrindo Métodos e Atributos

O CGM é muito mais do que apenas colorir classes e sugerir padrões para associações entre elas. Ele também propõe alguns métodos e atributos característicos para cada arquétipo. Por exemplo, um Momento-Intervalo (rosa) quase sempre terá um método CalcularValor ou CalcularTotal. Para avaliar a performance de um Papel (amarelo), usamos um método do tipo AvaliarPerformance, que retornará algum tipo de indicador que permita uma comparação (tipo “quem foi o melhor vendedor”, ou o “funcionário do mês”). Uma Descrição (azul) pode oferecer um método para calcular a quantidade disponível do item (tipo “estoque” de um produto).

De forma semelhante podemos descobrir muitos atributos característicos de cada arquétipo. Um MI com certeza terá uma Data (no caso de uma venda) ou um par DataInicial-DataFinal (para um empréstimo de livro). Pessoas, Lugares e Coisas (verde) normalmente terão uma identificação (código, endereço, etc.), assim como um Papel (a matrícula do funcionário, em nosso caso).

Com a experiência adquirida após algumas modelagens você notará essas recorrências. Anote suas observações e monte seu próprio Componente Genérico de Modelo, como se fosse uma biblioteca de classes (isto não é um trocadilho, mas a mais pura verdade!).

Fique à vontade para alterar o modelo proposto com suas próprias idéias para o estacionamento. Como já declarei antes, nunca se chega a um modelo ótimo nas primeiras rodadas de modelagem. É um processo de refinamentos sucessivos e uma ótima oportunidade para um trabalho em grupo.

    E a Aplicação?

Claro, para os programadores esta série de artigos tem sido uma tentação, pois até agora não vimos uma linha de código sequer! E isso é proposital! Temos que aprender a pensar um pouco mais antes de realmente colocar a mão na massa! Algumas rodadas de levantamento de requisitos e seções de modelagem costumam resultar em uma boa especificação de sistema, fazendo com que a equipe de projeto e os clientes tenham uma idéia mais consistente sobre o que deverá ser construído. Lembre-se que o custo de alteração de um requisito ou modelo é irrisório, comparado com o custo de uma alteração num componente ou módulo já implementado.

Mas creio que já temos um modelo maduro o suficiente para começarmos nossa aplicação. Conforme já explicado anteriormente, criaremos um projeto em ASP.NET, utilizando o Delphi 2006 com o framework ECO II.

Nota: Para criar a aplicação sugerida você deve ter o Delphi 2006 Architect e o InterBase 7.x instalados. Veja os links no final deste artigo para baixar as versões de avaliação. É possível também a utilização do MS SQL Server ou MSDE.

Crie um novo projeto através do menu File | New | Other, escolha a categoria Delphi for .NET Projects e o item ECO ASP.NET Web Application. Não entrarei em detalhes sobre aplicações ASP.NET, pois já foram publicados vários artigos sobre o assunto. Iremos nos concentrar no que é importante para o nosso contexto, a saber, a utilização do ECO como framework para aplicações OO. Na janela digite o nome do projeto, OOPark, e escolha o servidor HTTP que deseja utilizar para testar (no meu caso utilizo o Internet Information Server, IIS, mas também é possível utilizar o Cassini Web Server, que acompanha o Delphi 2006).

Hide image
Figura 3. Nova aplicação ECO ASP.NET

O projeto criado contém a página principal (WebForm1) e os arquivos normais (Web.config e Global.asax). Por causa do ECO existem mais 4 units: OOParkEcoSpace, EcoSpaceProvider, EcoPersistenceMapperProvider e CoreClassesUnit. Nesta última é que o código relativo às classes do modelo será gerado.

A primeira atividade será construir o modelo, se ainda não foi feito, ou modificá-lo, caso você já tenha iniciado no artigo anterior. Em ambos os casos, selecione o painel Model View, expanda o projeto OOPark, abra o pacote Package1 e dê um duplo-clique no diagrama Package1 (ou clique com o botão direito sobre ele e escolha Open Diagram). A superfície de desenho será mostrada e você poderá criar ou alterar o modelo de classes, conforme a Figura 2.

    Os Métodos de Negócio

Em algumas classes definimos métodos de negócio, como CalcValor na Estada. Para implementar esses métodos clique com o botão direito em sua declaração na classe e escolha a opção Goto Definition. O arquivo correspondente à classe será ativado e o cursor ficará posicionado na linha anterior (com o CustomAttribute) de onde o método está definido na interface da classe:

   |[UmlElement]
    function CalcValor(): Decimal;

Desça o cursor até a declaração do método e tecle Shift+Ctrl+Seta-abaixo, ou tecle Ctrl enquanto clica com o botão esquerdo no nome do método, para ir para o corpo correspondente.

function Estada.CalcValor(): Decimal;
begin
end;

Aí podemos digitar o código relativo ao método de negócio correspondente. Lembre-se que estamos dentro do contexto da classe, podendo utilizar os atributos, métodos e ligações disponíveis na mesma. Nada de preocupar-se com SQL, queries, etc. Tudo é objeto aqui!

Por exemplo, o método CalcValor e seu auxiliar CalcTempo podem ser codificados assim:

function Estada.CalcValor(): Decimal;
begin
  Result := Veiculo.CalcTotalPago(CalcTempo);
end;

function Estada.CalcTempo(): Integer;
const
  MinutosTolerancia = 15; // pode ser colocado em arquivo de configuração ou BD
var
  Duracao: TimeSpan;
  Fracao : Decimal;
begin
  // Calcula a duração da estada
  Duracao := (DataSaida + HoraSaida) - (DataEntrada + HoraEntrada);
  // Calcula a fração da hora
  Fracao := Decimal.Remainder(Duracao.TotalMinutes, 60);
  // Calcula número de horas 
  Result := Decimal.ToInt32(Duracao.TotalMinutes / 60);
  // Se a fração for maior que a tolerância, conta mais uma hora
  if Fracao > MinutosTolerancia then
    Inc(Result);
end;

Decepcionado ou maravilhado? Isso é orientação por objetos! Delegamos o cálculo para outra classe (distribuição de responsabilidades), ou seja, quem sabe qual valor deve pagar é o veículo, pois é ele que sabe qual o plano da tabela de preços correspondente (veja a Figura 2). Por sua vez, o método da classe Veículo pode ser implementado assim:

function Veiculo.CalcTotalPago(Tempo: Integer): Decimal;
begin
  if Preco.Item = 'Avulso' then
    Result := Preco.Valor * Tempo // valor da hora é homogêneo
  else
  if Preco.Item = 'Pernoite' then
    Result := Preco.Valor // valor do pernoite é fixo
  else
    Result := 0; // não cobrar para mensalistas e periodistas
end;

A delegação de responsabilidades nos permite pensar em etapas, resolvendo partes do problema, uma por vez. O bom senso e a experiência dirão qual o nível adequado para essa delegação, por razões de performance, clareza de código, etc.

Outro exemplo da facilidade oferecida pela ECO: as associações com outras classes aparecem como atributos. Se for uma associação 1-1, o atributo será do tipo da outra classe. Se for 1-N ou N-N, o atributo será uma lista de objetos da outra classe. Veja como ficaria a implementação do método CalcQtdeEstadas do Veiculo:

function Veiculo.CalcQtdeEstadas(): Integer;
begin
  Result := Estadas.Count;
end;

Ridículo, não? É só contar quantos objetos existem na lista de objetos da classe Estada. A associação entre Veiculo e Estada é representada por um atributo da classe Veiculo, chamado Estadas. Veja na Figura 2 que é o nome que está escrito no lado da associação com a Estada.

O método CalcScore do Cliente é bem interessante. Precisamos acumular todas as estadas de todos os veículos que o cliente possui. Ficou assim:

function Cliente.CalcScore(): Integer;
var
  Veic: Integer;
begin
  Result := 0;
  for Veic := 0 to Veiculos.Count do
    Inc(Result, Veiculos[Veic].Estadas.Count);
end;

Que beleza, hein?

    Onde os Objetos Serão Armazenados?

Montar o diagrama de classes e gerar o código correspondente até que não é uma tarefa complicada. Só que sem um framework, a coisa começa a complicar mesmo a partir daqui. Como gravar e recuperar os objetos em tempo de execução? O ECO permite utilizar qualquer conexão BDP (Borland Data Provider), SQL Server ou mesmo um arquivo XML. Entretanto, para aplicações onde o banco será compartilhado entre muitos clientes ou threads, o uso de XML não é suportado.

No nosso exemplo usaremos um banco no InterBase 7.x, mas as instruções são muito semelhantes para outros bancos. Crie um novo banco de dados usando o IBConsole (ou outra ferramenta de administração do InterBase). Basta criar o arquivo, de preferência no mesmo diretório do nosso projeto. Feito isto, crie uma conexão para esse banco no Data Explorer do Delphi 2006. Clique na seção InterBase com o botão direito e escolha Add New Connection. O Provider Name será InterBase e o Connection Name será OOPark. Clique OK. Expanda o nó InterBase, clique com o botão direito na conexão OOPark e escolha Modify Connection. No editor de conexão especifique o caminho completo para o arquivo do banco na propriedade Database. Ajuste a Password e o UserName, se necessário. Clique no botão Test para se certificar que a configuração está correta e, se tudo certo, clique OK.

Nota: é importante colocar o nome da máquina antes do caminho para o arquivo do banco, seguido do sinal dois-pontos. Se o servidor estiver na mesma máquina pode usar localhost:.

Abra a unit EcoPersistentMapperProvider e clique na aba Design (na parte inferior). Coloque um componente PersistenceMapperBDP (da categoria Enterprise Core Objects) e arraste a conexão que acabamos de criar (OOPark) para dentro do designer. Com isto indicamos para o ECO que queremos fazer a persistência dos objetos usando uma conexão BDP, a qual será feita através do componente BDPConnection criado ao arrastar e soltar a conexão OOPark.

Clique na parte branca do designer e certifique-se que a propriedade EcoSpaceType esteja apontando para OOParkEcoSpace.TOOParkEcoSpace, que é a classe que gerenciará o modelo. A propriedade PersistenceMapper deve apontar para o componente PersistenceMapperBDP1, que você colocou. Por sua vez, clique no PersistenceMapperBDP1 com o botão direito e escolha a opção InterBase [dialect 3] setup, para configurar o componente para usar o dialeto do InterBase. Verifique se a propriedade Connection está apontando para o BDPConnection1.

Se tudo estiver certo, podemos agora pedir para o ECO criar o esquema do banco de dados para nós! Clique no botão Generate Schema, ou clique com o botão direito na parte branca do designer e escolha a opção Generate Schema. Aparecerá uma janela mostrando que o ECO criará as tabelas necessárias. Clique OK e as tabelas serão criadas no banco de dados. Verifique-as abrindo a conexão OOPark no Database Explorer e expandindo o item Tables.

Hide image
Figura 4. Tabelas criadas automaticamente

Nada mal, hein? Explore as tabelas para ver como ficou o mapeamento Objeto-Relacional, especialmente a herança.

Agora, precisamos ligar o compartilhador de persistência com o nosso EcoSpace. Abra a unit OOParkEcoSpace e clique na aba Design. Clique no componente PersistenceMapperSharer1 e aponte a propriedade MapperProviderType para EcoPersistenceMapperProvider.TEcoPersistenceMapperProvider, que é a classe responsável pela persistência propriamente dita. Isso é necessário pois para cada requisição da página será criada uma thread, que exigirá uma cópia do EcoSpace para ela. Assim, teremos que compartilhar o espaço de objetos com todas as threads, cada uma obtendo sua cópia no início da requisição e devolvendo-a ao final da mesma.

    A Interface com o Usuário

Até agora nos concentramos na definição do negócio e na preparação da infra-estrutura de persistência. Vamos ver agora como apresentar os objetos para o usuário. Na página principal (WebForm1) você encontra um componente não-visual na parte inferior, chamado rhRoot. Esse componente é do tipo ReferenceHandle (todos os componentes do ECO ficam na categoria Enterprise Core Objects, na toolbar), que aponta para um objeto ou contexto. A propriedade EcoSpaceType especifica a classe do EcoSpace de onde o componente obterá o contexto. No nosso caso, precisamos indicar a classe OOParkEcoSpace.TOOParkEcoSpace, declarada na unit OOParkEcoSpace, que é o nosso espaço de objetos.

Nota: para que as referências às classes possam aparecer nos comboboxes do Object Inspector é necessário compilar o projeto.

Para obtermos referência a um objeto ou a uma lista de objetos, geralmente usamos um componente ExpressionHandle. Arraste um desses para a página e ajuste a propriedade Name para ehFuncionarios. Na propriedade RootHandle aponte para o rhRoot. Clique no botão da propriedade Expression para abrir o editor de expressões OCL (Object Constraint Language, Linguagem de Restrição de Objeto, parte da especificação da UML, utilizada para especificar expressões de restrições e consultas ao modelo), mostrado na Figura 5. A OCL pode ser entendida como o equivalente da SQL para objetos.

Hide image

Figura 5. Editor de expressões OCL

Nota: Leia este excelente artigo introdutório sobre OCL. Apesar de se referir ao Bold (Delphi 7), serve exatamente ao ECO.

Para montar uma expressão escolha uma das classes; para este exemplo, Funcionário. Dê um duplo-clique nela e imediatamente o nome aparecerá no lado esquerdo da janela. A lista do lado direito é alterada, mostrando diversos métodos. Como queremos uma lista com todos os objetos desta classe, escolhemos o método .allInstances (ou seja, todas as instâncias da classe), também com um duplo-clique. Dessa forma obtemos a expressão Funcionario.allInstances, que seria o equivalente a um select * from Funcionario. O editor nos avisa que a sintaxe está ok e que o resultado será uma coleção de objetos da classe Funcionário. Clique no botão OK para aceitar a expressão e observe que ela já aparece na propriedade Expression.

Para apresentar a lista na página, da categoria DB Web coloque um DBWebGrid e um EcoDataSource. Na propriedade EcoHandles deste último, clique no botão ... para abrir o editor de handles. Na lista da direita aparece o nosso ehFuncionarios. Selecione-o e clique no botão <. Clique em OK. Desta forma indicamos para o EcoDataSource quais ExpressionHandles ele irá referenciar.

Agora clique no DBWebGrid e aponte a propriedade DBDataSource para o EcoDataSource1. Em seguida, aponte a propriedade TableName para o ehFuncionarios. Imediatamente aparecerão algumas colunas no grid (se não aparecerem é porque faltou alguma coisa).

Hide image
Figura 6. Um grid mostrando as colunas da lista de objetos

Coloque também um DBWebNavigator na página, apontando a propriedade DBDataSource para o EcoDataSource1, e a propriedade TableName para o ehFuncionarios. Execute a aplicação com Shift+Ctrl+F9 (Run without debugging, execução sem depuração) e veja o resultado! Clique no botão + do navegador para criar alguns objetos. Volte no Delphi e execute a aplicação novamente para criar outra sessão. Note que os objetos criados no outro browser já aparecem aqui! Isso é fantástico!

Você também pode usar o navegador para selecionar e excluir objetos. O grid já faz paginação automática. Qualquer alteração nos atributos será salva assim que você mover a seleção no navegador, e pode ser vista nas outras seções imediatamente. Tudo isso sem uma linha de código nossa! Pode olhar na tabela Funcionario que as linhas estarão lá! Isso é Delphi, minha gente!!

    Cenas do Próximo Capítulo

Em nosso próximo encontro finalizaremos a aplicação, entrando em mais detalhes sobre a interface com o usuário e trabalhando com os métodos de negócio. Como lição de casa você deve tentar montar outras páginas para mostrar outras classes, aproveitando para se familiarizar com o editor de expressões OCL (faça uma visita ao ECO Center, na Welcome Page do Delphi 2006) e experimentando a OCL na prática. Uma revisão sobre ASP.NET também ajudará muito, já que não se trata de uma série sobre isso. Grande abraço e até mais!


Server Response from: SC1

 
Copyright© 1994 - 2008 Embarcadero Technologies, Inc. All rights reserved. Contact Us   Site Map   Legal Notices   Privacy Policy   Report Software Piracy