Orientação a Objetos no Delphi - Controle de Estoque (II)

7.780 visualizações

Publicada em

Publicada em: Software
0 comentários
3 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

Sem downloads
Visualizações
Visualizações totais
7.780
No SlideShare
0
A partir de incorporações
0
Número de incorporações
4.271
Ações
Compartilhamentos
0
Downloads
321
Comentários
0
Gostaram
3
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

Orientação a Objetos no Delphi - Controle de Estoque (II)

  1. 1. Orientação a Objetos no Delphi: Controle de Estoque – Parte II Ryan Bruno C Padilha ryan.padilha@gmail.com http://ryanpadilha.com.br Objetivo deste artigo Este artigo é o segundo de uma série de três artigos onde abordamos o paradigma orientado a objetos. No primeiro artigo foi introduzido os principais conceitos da orientação a objetos, sempre comparando a OO com o modelo de programação estrutural, permitindo assim ao leitor uma maior reflexão sobre o que seria interessante utilizar em projetos de software. Nesta segunda parte iniciaremos a construção de uma aplicação de controle de estoque básico, empregando todos os conceitos anteriormente abordados, provando na prática que a OO é viável, e até mesmo recomendada para projetos de média e grande complexidade. Utilizaremos este exemplo prático como referência, ou seja, uma orientação para o desenvolvimento de futuras aplicações, que poderão empregar todos os recursos que a orientação a objetos nos proporciona. 1. Descrição do Modelo Conceitual Ao iniciar um projeto de software abordando o paradigma orientado a objetos, seria interessante começarmos o processo de desenvolvimento através da modelagem das classes de negócios, identificando os elementos que fazem parte da aplicação. A modelagem pode ser realizada através da notação UML (Unified Modeling Language), que oferece um modelo de diagrama ideal para modelagem de classes – o diagrama de classe - do qual irá conter toda a lógica de interações entre os objetos que compõe o escopo do projeto. Nesta série sobre OO somente será adotado o diagrama de classes, considerado “a espinha dorsal” da UML e amplamente difundido entre os desenvolvedores, de fácil entendimento e utilização. Conforme citado no primeiro artigo, uma boa ferramenta para realizarmos a modelagem de classes, é o software Judy Community, que pode ser encontrado em http://jude.change- vision.com/jude-web/product/community.html. Existem outros diagramas na UML que poderiam ser adotados na modelagem da aplicação. São separados por categorias: 1) Diagramas Estruturais de: objeto, componentes, instalação, pacote, estrutura e classe; 2) Diagramas Comportamentais de: caso de uso, estado e atividade; 3) Diagrama de Interação (comportamentais) de: seqüência, interatividade, colaboração e sincronização. Muitos desenvolvedores neste momento podem se perguntar: o que é um controle de estoque? O controle de estoque tem como objetivo otimizar o investimento em estoques, aumentar o uso eficiente dos meios internos de uma empresa e minimizar as necessidades de capital investido em estoque. É uma área fundamental da empresa, pois através dela somos capazes de prever o quanto será necessário comprar de um determinado produto, além de outras informações úteis sobre as vendas, e a movimentação de entrada / saída de produtos do estoque. Os elementos básicos de uma aplicação de controle de estoque estão relacionados abaixo.
  2. 2. Trabalharemos com o domínio de uma aplicação voltada para o controle de estoque de produtos representado pela figura 1, que possui as seguintes classes: Endereco, Pessoa (classe abstrata), PessoaJ (classe abstrata), Empesa, Estoque, Pedido, Entrada, Produto, Marca, Unidade e Categoria. Observamos a presença da declaração de atributos e métodos em cada classe – os atributos possuem o modificador de visibilidade private, reforçando o conceito de encapsulamento de atributos, através de métodos acessores do tipo getter e setter (retornando e atribuindo valores, respectivamente), alterando o estado do objeto instanciado. Os métodos acessores são utilizados na declaração Property (propriedade), sobrecarregando as operações de leitura (read) e escrita (write) executadas quando a propriedade for acessada. A declaração citada desempenha papel fundamental ao proteger o acesso ao objeto, definindo uma forma padronizada de acesso ao estado do objeto (as propriedades e métodos getters/setters foram omitidos do diagrama de classes apresentado na figura 1) . O atributo TABLENAME é estático, cada classe concreta poderá (em nosso diagrama deve) possuir um determinado nome de tabela, que deverá ser mapeado para uma tabela no modelo de banco de dados objeto-relacional. Além dos métodos acessores sobrecarregados por Property, é declarado e implementado um conjunto de métodos que realizam operações sobre o SGBD (Sistema Gerenciador de Banco de Dados) executando comandos SQL/DML (Structured Query Language/Data Manipulation Language): 1) SELECT – Selecionar objetos persistidos em uma tabela, serializando as colunas em atributos de classe. Os métodos assinados como getObject(Id: String): boolean e getObjects(): boolean recuperam objetos do SGBD; 2) INSERT – Persistir objetos em uma tabela, serializando seu atributos em colunas. O método assinado como public Merge(): boolean verifica se o atributo código do objeto está com presença de valor (diferente de vazio/0), se o mesmo não contiver nenhum valor o comando INSERT será executado através da chamada do método private Insert(): boolean; 3) UPDATE – Atualizar objetos em uma tabela, que foram selecionados anteriormente (des- serializados) e estão instanciados na memória principal; empregando um filtro WHERE que dispõem de condições comparativas, neste caso usamos o identificador único do objeto, o Id (código) que é chave primária da tabela onde o objeto está sendo recuperado/persistido. O método assinado como public Merge(): boolean chamado para executar o comando INSERT é utilizado também para a execução do comando UPDATE, se o atributo código do objeto recuperado for consistente o método private Update(): boolean é invocado; 4) DELETE – Deletar objetos em uma tabela. Um objeto recuperado do SGBD e instanciado na memória principal, pode ser excluído através da chamada do método Delete(): boolean executando o comando DELETE. Usamos como referência o identificador único do objeto, o Id (código), citado anteriormente. O conjunto das quatro operações básicas relacionadas acima empregadas em SGBD objeto- relacional, reconhecido como CRUD (acrônimo de Create, Retrieve, Update, Delete) é implementado na maioria das aplicações que adotam o paradigma orientado a objetos. É notado que o CRUD contém assinaturas de métodos comuns na maioria das classes concretas em que estão implementados, e possuem comportamento semelhante - o de executar instruções SQL. Poderemos em um próximo artigo descrever a arquitetura e implementar uma biblioteca de classes que se relacionam entre si através de associações e composições, com a
  3. 3. finalidade de automatizar o processo de Mapeamento Objeto-Relacional ao adotar o paradigma orientação a objetos. A tarefa de mapear objetos em entidade relacional é algo na maioria das vezes repetitivo, onde serializamos e des-serializamos objetos, ou seja, mapeando atributo-coluna / coluna-atributo em determinada tabela presente no Schema do usuário do SGBD. Figura 1 – Diagrama de classes simplificado. Domínio: Controle de Estoque. 2. Implementação em Object Pascal do diagrama de classes de domínio Este exemplo tem finalidade educacional e pode ser modificado, alterado e distribuído. Caso seja utilizado para fins didáticos por outras pessoas, preserve o nome do autor. Como exemplo para esta série de artigos, criaremos uma aplicação conforme apresentado pelo diagrama da figura 1. Adotamos a IDE Delphi 7 (mais nada impede a utilização de outras versões do Delphi), o intuito é apresentar os conceitos da orientação a objetos através de uma implementação de um software real, com a finalidade de controlar o estoque de produtos de uma empresa do ramo varejista e/ou atacadista. Para realizar a persistência de objetos, ou seja, efetuar a gravação permanente de objetos para a futura recuperação dos mesmos, e até para manter um histórico de tudo o que ocorreu até hoje com o estoque de uma empresa, é necessário a utilização de um SGBD.
  4. 4. O SGBD PostgreSQL 8.3, banco de dados objeto-relacional, é adotado neste exemplo por ser open-source (não é necessário adquirir licença para utilizá-lo), de fácil instalação e manutenção, robusto, confiável, flexível, rico em recursos, dentre outras características. Oferece suporte a comandos complexos, chaves estrangeiras, gatilhos (triggers), visões, integridade transacional, e controle de simultaneidade multiversão. Pode ser encontrado em http://www.postgresql.org. Não será exibido aqui como instalar o PostgreSQL, sua instalação é simples e rápida. Caso haja alguma dúvida em como efetuar a instalação, procure informações em fóruns e websites especializados neste banco de dados. Um ponto importante que deve ser analisado com cuidado é: qual componente de acesso a dados pode ser utilizado para acessar os dados no PostgreSQL? Como muitos desenvolvedores que estão lendo este artigo trabalham com diversos ambientes de banco de dados, é interessante escolher um componente de acesso que ofereça uma maior flexibilidade e facilidade durante a utilização e/ou migração de um banco de dados para outro. Tendo isto como requisito básico de projeto, podemos utilizar a biblioteca ZeosLib que é open-source (assim como o PostgreSQL), suporta conexão nativa e transparente, composto por um conjunto de componentes de banco de dados que oferece suporte a diversos banco de dados: MySQL, PostgreSQL, Firebird, MSSQL Server, Oracle e SQLite. Para quem ainda não tem o ZeosLib instalado no Delphi, efetue o download em http://sourceforge.net/projects/zeoslib/, durante a escrita deste artigo, a versão estável disponível para download do ZeosLib era ZEOSDBO-6.6.6-stable, porém no exemplo que será desenvolvido foi utilizado a versão 6.6.2- RC. Caso tenha alguma dúvida entre as diversas versões disponíveis e a diferença entre elas, leia o File Releases do projeto ZeosLib. Sua instalação é relativamente simples, bastando seguir um passo-a-passo no estilo “receita de bolo” e pronto. Para maiores detalhes sobre a instalação veja em http://www.activedelphi.com.br/forum/viewtopic.php?t=33645. Não será exibido aqui como instalar a biblioteca ZeosLib, pois pode ser encontrado no próprio Active Delphi informações de como proceder. Figura 2 – Biblioteca ZeosLib no Delphi 7 (aba ZeosAccess). Após adquirir o SGBD PostgreSQL e o componente de acesso a dados ZeosLib instalados no ambiente de trabalho, podemos iniciar o projeto de software. Com o Delphi aberto crie um novo projeto chamado “ControleEstoque” (menu File – New – Application) e salve-o no diretório “d:projetosoocontrole_estoque” ou escolha um local de sua preferência. No formulário principal que é exibido na tela adicione o seguinte componente visual: - TMainMenu: Adicione o menu Cadastros contendo os itens: Grupo de Empresas, Produtos. Neste últimos criaremos um sub-menu contendo: Cadastro de Produto, Cadastro de Marca, Cadastro de Unidade e Cadastro de Categoria. Adicione o menu Movimentação contendo o item Movimentação de Estoque. E por último adiciona o menu Sobre. A princípio estamos modelando a apresentação visual de nossa aplicação, definindo o formulário principal e seu menu de navegação, que fornecerá acesso aos formulários da
  5. 5. aplicação que serão criados posteriormente. Dentro do diretório do projeto crie um diretório “classes” – com a função de agrupar as classes de negócio implementadas no projeto; e outro diretório “view” – utilizado com repositório de formulários construídos através do designer. Os diretórios criados atuam como pacotes, armazenando código-fonte semelhantes, ou seja, todas as nossas classes estarão contidas dentro de “classes” e os formulários dentro de “view”, com isso obtemos uma maior organização. Se futuramente quisermos reaproveitar nossas classes de negócios em outro projeto, será fácil localizar e compartilhar o mesmo código-fonte com outro projeto de software que se beneficiaria da implementação realizada neste módulo de controle de estoque, promovendo a reutilização de código – característica fundamental da orientação a objetos. 2.1 Classes de negócio No projeto de software modelamos as classes de negócio, cada classe é implementada em uma unit, por exemplo a classe Endereco é definida na unit clEndereco e assim por diante. É adotado uma convenção de código, definindo um padrão para a codificação, melhorando com isso a legibilidade do código-fonte. Segue abaixo a convenção utilizada aqui: 1) clNomeUnit: define o nome da unit que contém a implementação da classe. Exemplo: clEndereco, clPessoa, clPessoaJ, clEmpresa, clEstoque, clProduto. 2) TNomeClasse: define o nome da classe. Toda classe definida no Delphi inicia com a letra T. Exemplo: TEndereco, TPessoa, TPessoaJ, TEmpresa, TEstoque, TProduto. 3) _nomeAtributo: define o nome de atributo. Exemplo: _codigo, _logradouro, _numero, _nome. 4) NomeProperty: define o nome da propriedade. A primeira letra em maiúsculo e o restante da palavra em minúsculo. Exemplo: Codigo, Logradouro, Numero, NomeComp. Comecemos com a implementação da classe concreta Endereco, a responsabilidade dessa classe é conter dados relacionado aos endereços da classe abstrata Pessoa. Adicione uma nova unit ao projeto através de Menu File – New – Unit, salve a unit no diretório “classes” como clEndereco.pas. No bloco interface/implementation da unit declare/implemente a classe conforme abaixo: unit clEndereco; interface type TEndereco = class(TObject) private // métodos acessores suprimidos. getters / setters. protected // declaração de atributos _codigo: String; _logradouro: String; _numero: String; _complemento: String; _cep: String; _tipo: Integer;
  6. 6. _correspondencia: Boolean; _referencia: String; _bairro: String; _cidade: String; public // declaração das propriedades da classe, encapsulamento de atributos property Codigo: String read getCodigo write setCodigo; property Logradouro: String read getLogradouro write setLogradouro; property Numero: String read getNumero write setNumero; property Complemento: String read getComplemento write setComplemento; property Cep: String read getCEP write setCEP; property Tipo: Integer read getTipo write setTipo; property Correspondencia: Boolean read getCorrespondencia write setCorrespondencia; property Referencia: String read getReferencia write setReferencia; property Bairro: String read getBairro write setBairro; property Cidade: String read getCidade write setCidade; // nesta última linha pressione Ctrl + Shift + C // para gerar os métodos acessores getters e setters automaticamente // declaração de métodos function Validar(): boolean; function Merge(): Boolean; function Delete(Filtro: String): Boolean; function getObject(Id: String; Filtro: String): Boolean; function Insert(Id: String; Tipo: String): Boolean; function Update(Id: String): Boolean; end; const TABLENAME = 'ENDERECO'; implementation uses clUtil, SysUtils, dmConexao, DB, ZDataset, ZAbstractRODataset, Variants; { TEndereco } function TEndereco.Delete(Filtro: String): Boolean; begin try Result := False; with Conexao.QryCRUD do begin Close; SQL.Clear; SQL.Text := 'DELETE FROM '+ TABLENAME +' WHERE END_CODIGO =:CODIGO'; ParamByName('CODIGO').AsString := Self.Codigo; ExecSQL; end; Result := True; Except on E : Exception do ShowMessage('Classe: '+ e.ClassName + chr(13) + 'Mensagem: '+ e.Message); end; end;
  7. 7. function TEndereco.getBairro: String; begin Result := _bairro; end; function TEndereco.getCEP: String; begin Result := _cep; end; function TEndereco.getCidade: String; begin Result := _cidade; end; function TEndereco.getCodigo: String; begin Result := _codigo; end; function TEndereco.getComplemento: String; begin Result := _complemento; end; function TEndereco.getCorrespondencia: Boolean; begin Result := _correspondencia; end; function TEndereco.getLogradouro: String; begin Result := _logradouro; end; function TEndereco.getNumero: String; begin Result := _numero; end; function TEndereco.getObject(Id: String; Filtro: String): Boolean; begin Try Result := False; if TUtil.Empty(Id) then Exit; with Conexao.QryGetObject do begin Close; SQL.Clear; SQL.Add('SELECT * FROM '+ TABLENAME); if filtro = 'ENDERECO' then begin SQL.Add(' WHERE END_CODIGO =:CODIGO');
  8. 8. ParamByName('CODIGO').AsString := Id; end else if filtro = 'EMPRESA' then begin SQL.Add(' WHERE EMP_CODIGO =:CODIGO'); ParamByName('CODIGO').AsString := Id; end; Open; First; end; if Conexao.QryGetObject.RecordCount > 0 then begin Self.Codigo := Conexao.QryGetObject.FieldByName('END_CODIGO').AsString; Self.Logradouro := Conexao.QryGetObject.FieldByName('END_LOGRADOURO').AsString; Self.Numero := Conexao.QryGetObject.FieldByName('END_NUMERO').AsString; Self.Complemento := Conexao.QryGetObject.FieldByName('END_COMPLEMENTO').AsString; Self.Bairro := Conexao.QryGetObject.FieldByName('END_BAIRRO').AsString; Self.Cidade := Conexao.QryGetObject.FieldByName('END_CIDADE').AsString; self.Cep := Conexao.QryGetObject.FieldByName('END_CEP').AsString; Self.Referencia := Conexao.QryGetObject.FieldByName('END_PT_REFERENCIA').AsString; Self.Tipo := Conexao.QryGetObject.FieldByName('END_TIPO').AsInteger; Self.Correspondencia := Conexao.QryGetObject.FieldByName('END_CORRESP').AsBoolean; Result := True; end else ShowMessage('Registro não encontrado!'); Except on E : Exception do ShowMessage('Classe: '+ e.ClassName + chr(13) + 'Mensagem: '+ e.Message); end; end; function TEndereco.getReferencia: String; begin Result := _referencia; end; function TEndereco.getTipo: Integer; begin Result := _tipo; end; function TEndereco.Insert(Id: String; Tipo: String): Boolean; begin Try Result := False; with Conexao.QryCRUD do begin Close; SQL.Clear; SQL.Text := 'INSERT INTO '+ TABLENAME +' (END_LOGRADOURO, END_NUMERO, END_COMPLEMENTO, END_CEP, END_TIPO, END_CORRESP, END_PT_REFERENCIA, '+ ' END_BAIRRO, END_CIDADE, EMP_CODIGO) '+
  9. 9. ' VALUES(:LOGRADOURO, :NUMERO, :COMPLEMENTO, :CEP, :TIPO, :CORRESP, :REFERENCIA, :BAIRRO, :CIDADE, :EMPRESA) '; ParamByName('LOGRADOURO').AsString := Self.Logradouro; ParamByName('NUMERO').AsString := Self.Numero; ParamByName('COMPLEMENTO').AsString := Self.Complemento; ParamByName('CEP').AsString := Self.Cep; ParamByName('TIPO').AsInteger := Self.Tipo; ParamByName('CORRESP').AsBoolean := Self.Correspondencia; ParamByName('REFERENCIA').AsString := Self.Referencia; ParamByName('BAIRRO').AsString := Self.Bairro; ParamByName('CIDADE').AsString := Self.Cidade; if Tipo = 'EMPRESA' then ParamByName('EMPRESA').AsString := Id; ExecSQL; end; Result := True; Except on E : Exception do ShowMessage('Classe: '+ e.ClassName + chr(13) + 'Mensagem: '+ e.Message); end; end; function TEndereco.Merge: Boolean; begin // nada implementado aqui, INSERT e UPDATE executados diretamente Result := True; end; procedure TEndereco.setBairro(const Value: String); begin _bairro := Trim(Value); end; procedure TEndereco.setCEP(const Value: String); begin _cep := Trim(Value); end; procedure TEndereco.setCidade(const Value: String); begin _cidade := Trim(Value); end; procedure TEndereco.setCodigo(const Value: String); begin _codigo := Value; end; procedure TEndereco.setComplemento(const Value: String); begin _complemento := Trim(Value);
  10. 10. end; procedure TEndereco.setCorrespondencia(const Value: Boolean); begin _correspondencia := Value; end; procedure TEndereco.setLogradouro(const Value: String); begin _logradouro := Trim(Value); end; procedure TEndereco.setNumero(const Value: String); begin _numero := Trim(Value); end; procedure TEndereco.setReferencia(const Value: String); begin _referencia := Trim(Value); end; procedure TEndereco.setTipo(const Value: Integer); begin _tipo := Value; end; function TEndereco.Update(Id: String): Boolean; begin Try Result := False; with Conexao.QryCRUD do begin Close; SQL.Clear; SQL.Text := 'UPDATE '+ TABLENAME +' SET END_LOGRADOURO =:LOGRADOURO, END_NUMERO =:NUMERO, '+ ' END_COMPLEMENTO =:COMPLEMENTO, END_CEP =:CEP, END_TIPO =:TIPO, END_CORRESP =:CORRESP, '+ ' END_PT_REFERENCIA =:REFERENCIA, END_BAIRRO =:BAIRRO, END_CIDADE =:CIDADE '+ ' WHERE END_CODIGO =:CODIGO'; ParamByName('LOGRADOURO').AsString := Self.Logradouro; ParamByName('NUMERO').AsString := Self.Numero; ParamByName('COMPLEMENTO').AsString := Self.Complemento; ParamByName('BAIRRO').AsString := Self.Bairro.Codigo; ParamByName('CIDADE').AsString := Self.Cidade.Codigo; ParamByName('CEP').AsString := Self.Cep; ParamByName('REFERENCIA').AsString := Self.Referencia; ParamByName('TIPO').AsInteger := Self.Tipo; ParamByName('CORRESP').AsBoolean := Self.Correspondencia; ParamByName('CODIGO').AsString := Self.Codigo; ExecSQL; end; Result := True;
  11. 11. Except on E : Exception do ShowMessage('Classe: '+ e.ClassName + chr(13) + 'Mensagem: '+ e.Message); end; end; function TEndereco.Validar: boolean; begin Result := True; end; end. Prestem atenção, implementamos nossa classe TEndereco conforme o diagrama de classes da figura 1, assim será feito com as outras classe de nosso domínio. A unit acima utiliza-se de componentes de acesso a dados presentes em um DataModule, que fornece meios para efetuar operações CRUD sobre o SGBD. Então vamos criar um módulo de acesso a dados conforme abaixo: 1) Adicione um novo DataModule ao projeto através do Menu File – New – DataModule. 2) Salve a unit do DataModule como dmConexao.pas, e defina a propriedade Name do mesmo como sendo Conexao. 3) Adicione os seguintes componentes da paleta ZeosAccess: 1 TZConnection (ZConnection): Realiza a conexão com o banco de dados; Propriedades Connected: false Database: ActiveDelphi [nome do banco] Hostname: localhost [endereço do servidor do banco] Password: [password do usuário do banco] Port: 5432 (default) Protocol: postgresql-7 User: [nome do usuário do banco] 3 TZQuery (QryCRUD, QryGetObject, QryGeral): Executam comando SQL; Propriedades Connection: ZConnection [nome do componente de conexão] Entre a classe Endereco e Pessoa temos uma associação de *..1, onde uma pessoa pode conter 1 ou vários endereços (residencial, comercial, de cobrança, etc). Na orientação a objetos falamos muito sobre herança de implementação e encontramos isso no diagrama da figura 1. Pessoa é uma classe abstrata que não pode ser instancia diretamente por um objeto, utilizada apenas como uma classe mais genérica, ou seja, servindo como base para a definição de uma hierarquia de classes. Sendo uma classe mais “genérica”, ela é superclasse da classe abstrata PessoaJ que é responsável por conter dados relacionados a pessoas jurídicas. A classe Empresa
  12. 12. é uma classe mais especialista, é uma subclasse de PessoaJ herdando todas as suas características, que conseqüentemente está herdando da classe Pessoa. A organização e reutilização de código é notável neste momento, pois com a definição de classes mais genéricas, podemos estender suas características através de classes mais especialistas. O escopo da aplicação é o controle de estoque, abrangendo apenas o diagrama apresentado na figura 1. Porém poderíamos desenvolver um módulo de cadastros, para controlar fornecedores e clientes de uma empresa, por exemplo. Para isso bastaria apenas declarar outro conjunto de classes tal qual a classe de Fornecedores e Clientes que são classes (especialistas) pessoas do tipo Pessoa Física ou Pessoa Jurídica. Como no modelo já adicionamos a classe PessoaJ, apenas é necessário adicionar uma classe do tipo PessoaF (pessoa física), e uma classe Fornecedor/Cliente, do qual devem herdar todas as características das classes Pessoa, conseqüentemente de PessoaJ ou PessoaF. A classe abstrata Pessoa possui a responsabilidade de conter apenas a definição de atributos comuns a todas as pessoas existentes no mundo real. Seu papel é servir apenas como base para outra classe mais especialista. Adicione uma nova unit ao projeto através do Menu File – New – Unit, salve a unit no diretório “classes” como clPessoa.pas. No bloco interface da unit declare a classe conforme abaixo: unit clPessoa; interface uses clEndereco; type TPessoa = class(TObject) private // métodos acessores suprimidos. getters / setters. protected // atributos _telefone: String; _fonefax: String; _celular: String; _email: String; _pagina: String; _observacao: WideString; _status: Integer; _endereco: TEndereco; _dtCadastro: TDateTime; _dtAlteracao: TDateTime; public // propriedades property Telefone: String read getTelefone write setTelefone; property FoneFax: String read getFoneFax write setFoneFax; property Celular: String read getCelular write setCelular; property Email: String read getEmail write setEmail; property Pagina: String read getPagina write setPagina; property Observacao: WideString read getObservacao write setObservacao; property Status: Integer read getStatus write setStatus; property Endereco: TEndereco read getEndereco write setEndereco; property DtCadastro: TDateTime read getDtCadastro write setDtCadastro;
  13. 13. property DtAlteracao: TDateTime read getDtAlteracao write setDtAlteracao; end; implementation // implementação suprimida, para não estender muito o artigo end. Encontramos no diagrama outra classe abstrata, a PessoaJ que tem como responsabilidade a definição de atributos comuns ao tipo jurídico de pessoas (empresas que possuem a inscrição CNPJ). A implementação de PessoaJ herda todas as características da superclasse Pessoa. Adicione uma nova unit ao projeto através do Menu File – New – Unit, salve a unit no diretório “classes” como clPessoaJ.pas. No bloco interface da unit declare a classe conforme abaixo: unit clPessoaJ; interface uses clPessoa; type TPessoaJ = class(TPessoa) // classe herda de TPessoa private // métodos acessores suprimidos. getters / setters. protected _razao: String; _fantasia: String; _cnpj: String; _ie: String; _iest: String; _im: String; _cnae: String; _crt: Integer; _alias: String; _dtAbertura: TDateTime; _contato: String; public // propriedades property Razao: String read getRazao write setRazao; property Fantasia: String read getFantasia write setFantasia; property CNPJ: String read getCNPJ write setCNPJ; property IE: String read getIE write setIE; property IEST: String read getIEST write setIEST; property IM: String read getIM write setIM; property Cnae: String read getCnae write setCnae; property Crt: Integer read getCrt write setCrt; property Alias: String read getAlias write setAlias; property DtAbertura: TDateTime read getDtAbertura write setDtAbertura; property Contato: String read getContato write setContato; end; implementation // implementação suprimida, para não estender muito o artigo end.
  14. 14. Observamos que há uma hierarquia de classes, onde temos na base uma classe mais “genérica” e no final uma classe mais “especialista”. A classe concreta Empresa pode ser instanciada diretamente, responsável pela definição de atributos comuns a todas as empresas do mundo real. Em sua implementação notamos que esta classe possui apenas um atributo (codigo), pois o restante foi herdado através da hierarquia de classes citada anteriormente. Adicione uma nova unit ao projeto através do Menu File – New – Unit, salve a unit no diretório “classes” como clEmpresa.pas. Nesta unit declaramos a classe conforme abaixo: unit clEmpresa; interface uses clPessoaJ, clEndereco; type TEmpresa = class(TPessoaJ) private // métodos privados // métodos acessores suprimidos. getters / setters. function Insert(): Boolean; function Update(): Boolean; protected _codigo: String; public constructor Create; property Codigo: String read getCodigo write setCodigo; // métodos publicos function Validar(): Boolean; function Merge(): Boolean; function Delete(): Boolean; function getObject(Id: String): Boolean; procedure getMaxId; end; const TABLENAME = 'EMPRESA'; implementation // implementação suprimida, para não estender muito o artigo end. Agora que temos uma parte do diagrama de classes implementado no Object Pascal, vamos construir o formulário que irá persistir os objetos do tipo Empresa. Não será especificado aqui todos os componentes visuais e suas respectivas propriedades, tomemos como base para a construção de nosso formulário o layout da figura 3.
  15. 15. Figura 3 – Formulário de cadastro de grupo de empresas. Com o formulário criado, basta apenas adicionar os eventos necessários para inserir, editar, visualizar e deletar registros de uma empresa, vamos criar o banco de dados a ser utilizado neste exemplo. Como adotamos o SGBD PostgreSQL podemos utilizar a ferramenta gráfica pgAdminIII (compõe o instalador do banco) para a criação do banco de dados e das tabelas onde os objetos da aplicação serão persistidos. Execute a ferramenta pgAdminIII, com ela aberta dê dois cliques no hostname onde será criado o banco de dados, realize o login, forneça apenas a senha que foi definida ao usuário do banco na instalação do mesmo. Com o botão direito do mouse clique em Banco de Dados – Novo Banco de Dados. Será exibida uma caixa de diálogo, preencha conforme exibido na figura 4, e clique no botão OK para confirmar a criação da base de dados chamada “ActiveDelphi”. Agora falta criar as tabelas onde os objetos serão persistidos, ou seja, serializados. Através do editor SQL (clicando no ícone SQL) execute os seguintes comandos SQL DDL (Data Definition Language): Nota: Caso não retorne nenhum erro, os comandos SQL foram executados com sucesso no PostgreSQL. -- EMPRESA CREATE TABLE EMPRESA( EMP_CODIGO SERIAL NOT NULL, EMP_RAZAO VARCHAR(50),
  16. 16. EMP_FANTASIA VARCHAR(50), EMP_DT_CADASTRO DATE, EMP_DT_ABERTURA DATE, EMP_STATUS INTEGER, EMP_ALIAS VARCHAR(50), EMP_TELEFONE VARCHAR(14), EMP_FONEFAX VARCHAR(14), EMP_CNPJ VARCHAR(20), EMP_IE VARCHAR(20), EMP_IEST VARCHAR(20), EMP_IM VARCHAR(20), EMP_CRT INTEGER, EMP_CNAE VARCHAR(10), EMP_EMAIL VARCHAR(50), EMP_PAGINA VARCHAR(50), EMP_CONTATO VARCHAR(50), EMP_MENSAGEM TEXT ); ALTER TABLE EMPRESA ADD CONSTRAINT PK_EMPRESA PRIMARY KEY(EMP_CODIGO); -- ENDERECO CREATE TABLE ENDERECO( END_CODIGO SERIAL NOT NULL, END_LOGRADOURO VARCHAR(100) NOT NULL, END_NUMERO VARCHAR(10) NOT NULL, END_COMPLEMENTO VARCHAR(50), END_CEP VARCHAR(10), END_TIPO INTEGER, END_CORRESP BOOLEAN, END_PT_REFERENCIA VARCHAR(200), END_BAIRRO VARCHAR(100), END_CIDADE VARCHAR(100) ); ALTER TABLE ENDERECO ADD CONSTRAINT PK_ENDERECO PRIMARY KEY(END_CODIGO); ALTER TABLE ENDERECO ADD COLUMN EMP_CODIGO INTEGER; ALTER TABLE ENDERECO ADD CONSTRAINT FK_END_EMPRESA FOREIGN KEY(EMP_CODIGO) REFERENCES EMPRESA(EMP_CODIGO);
  17. 17. Figura 4 – Criação do banco de dados ‘ActiveDelphi’ utilizando o pgAdminIII No formulário da figura 3, podemos utilizar a classe Empresa declarando uma variável do tipo TEmpresa no bloco Interface/var da unit. Sendo assim, todos os dados que forem digitados no formulário serão passados a este objeto instanciado, posteriormente persistido no SGBD. Fica claro que os formulários presentes neste projeto apenas são projetados para a entrada e saída de dados (visualização), pois a regra de negócios e processamento de dados fica sob a responsabilidade da classe implementada dentro do “pacote classes”. Não estamos trabalhando com o padrão de projeto MVC (Model-View-Controller), pois na aplicação exemplo não implementamos o Controller, que controla a interação entre a camada View e Model. Resumidamente trabalharemos diretamente com a View (interface gráfica) e o Model (modelo de negócio, classes). Veja abaixo como declarar uma variável Empresa do tipo TEmpresa definida na aplicação: unit untCadastroEmpresa; interface // declaração uses e classe do formulário suprimidas var Empresa: TEmpresa; No evento onShow do formulário poderíamos instanciar o objeto Empresa declarado em var, conforme abaixo: procedure TFrmCadastroEmpresa.FormShow(Sender: TObject); begin Empresa := TEmpresa.Create; // restante do código suprimido end;
  18. 18. Para simplificar este artigo, não será exibido o código-fonte dos eventos do formulário apresentado na figura 3, porém nossa aplicação de controle de estoque implementada até agora pode ser baixada em http://ryanpadilha.com.br/downloads/active_delphi/controle_estoque_parte1.rar. Todos os eventos de botões, validação de dados, instanciação/recuperação de objetos do tipo Empresa, chamada de métodos de objetos, estão presentes no arquivo disponibilizado para download. Esta aplicação utiliza componentes TEdit que não possuem vínculo direto com um DataSet em particular, ou seja, com acesso direto ao banco de dados tal como os componentes do estilo TDBEdit. Então você está livre para alterá-lo conforme a sua necessidade e vontade. 3. Conclusão O paradigma orientado a objetos, exposto aqui na prática, através da construção da primeira parte de uma aplicação de controle de estoque básica, permite claramente uma maior organização de código, ou seja, as alterações futuras serão realizadas de forma fácil, pois temos uma clara visão sobre as responsabilidades de cada classe e suas interações. A manutenção (alteração) de projetos de software é um processo custoso, doloroso e na maioria das vezes produz um produto de baixa qualidade, pois as modificações podem não ser testadas adequadamente, resultando em re-trabalho pela equipe de desenvolvimento. A parte prática complementa o que vimos na teoria através do primeiro artigo, que expos uma série de vantagens em relação a obtenção de qualidade em projetos de software. Esta segunda parte propôs a modelagem do domínio de uma aplicação através da notação UML, que oferece uma documentação padronizada e simplificada do projeto de software. Adotando o diagrama de classes, implementamos classes no Delphi, relembrando os conceitos abordados no primeiro artigo, porém com um foco maior na implementação das classes e sua interação com outras classes. No terceiro e último artigo, será exposto como trabalhar com objetos de tipos definidos (classes) em um formulário, exibindo a forma como o objeto opera com os campos do formulário e como o mesmo pode ser chamado por outro formulário. Caso tenha alguma dúvida entre em contato comigo. Será um prazer ajudar. Até a terceira e última parte da série sobre a orientação a objetos. Forte Abraço!

×