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

6,302
-1

Published on

Published in: Software
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
6,302
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
218
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

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

  1. 1. Orientação a Objetos no Delphi: Controle de Estoque – Parte Final Ryan Bruno C Padilha ryan.padilha@gmail.com http://ryanpadilha.com.br Objetivo deste artigo Este artigo é a parte final de uma série de três artigos onde abordamos o paradigma orientado a objetos. No segundo artigo iniciamos a construção de uma aplicação de controle de estoque básico, provando na prática que a OO não possui estruturas complexas de dados; ao contrário, contempla um modelo simplificado de um conjunto de classes que se relacionam entre si, seja através de heranças ou associações de agregação/composição. Nesta terceira parte será revisto o modelo de domínio do controle de estoque, reforçando a utilização da notação UML, que oferece uma documentação padronizada e simplificada do projeto de software. A implementação em Object Pascal do restante das classes de domínio é realizado com base na documentação referida acima, porém o objetivo agora é definir o comportamento dos objetos de negócio instanciados na memória principal sobre a camada de visualização de dados (view/formulário), exibindo a forma como operam com os campos do formulário e como podem ser invocados por outro formulário. 1. Revisão do Modelo Conceitual O modelo de domínio abordado é o controle de estoque, do qual tem por finalidade em síntese otimizar o investimento em estoques, minimizando a necessidade de capital investido. Nos artigos anteriores discutimos e implementamos parte do diagrama de classes exibido na figura 1, ou seja, na segunda parte desta série implementamos no ambiente Delphi as classes: Endereco, Pessoa, PessoaJ e Empresa; e construímos o formulário principal da aplicação, bem como o formulário de cadastro do grupo de empresas (view). Com o referido cadastro em funcionamento podemos cadastrar as empresas que terão controle sobre seus estoques de produtos, realizando a entrada e saída dos mesmos seja através do formulário de manutenção de estoque ou mesmo pela entrada de notas fiscais e/ou pela operação de venda ao consumidor final (estes dois últimos itens citados não fazem parte do escopo do artigo, foram apenas citados por realizarem operações sobre a quantidade de produtos em estoque). O propósito agora é implementar as classes: Marca, Unidade, Produto e Estoque; as demais classes Categoria, Pedido e Entrada não serão implementadas neste momento para não fugirmos demasiadamente do objetivo do artigo e para simplificar a implementação. Conforme citado no segundo artigo, uma boa ferramenta para realizar a modelagem de classes, é o software Jude Community (encontrado em http://jude.change-vision.com/jude- web/product/community.html), porém infelizmente o projeto desta ferramenta de modelagem foi descontinuado pela empresa ChangeVision que a mantinha. Os leitores que tiveram o interesse em baixar a ferramenta e ver como a mesma funciona ficaram frustrados. Mas nem tudo está perdido, como é de costume a comunidade de desenvolvedores reivindicou pela ferramenta de modelagem UML e a empresa mantenedora do projeto retomou este projeto renomeando-o como Astah*Community, que pode ser encontrado em http://astah.change-vision.com/en/product/astah-community.html.
  2. 2. A ferramenta Astah*Community é gratuita, suporta notação UML 2.0, suprindo a necessidade de grande parte dos elementos necessários no dia-a-dia. O diagrama de classes da figura 1, foi modelado utilizando o Astah* versão 6.4, caso queira baixar o arquivo do diagrama de classes do modelo conceitual de controle de estoque, o mesmo pode ser encontrado em http://ryanpadilha.com.br/downloads/active_delphi/UML_controle_estoque.jude. Seria interessante baixar o diagrama citado, aprender a utilizar a ferramenta, assim como identificar os elementos básicos de um diagrama de classe, pois para os leitores que acompanham os artigos do colunista que os escreve, futuramente abordaremos outros conceitos referentes a arquitetura e engenharia de software utilizando como base o modelo acima. É válido observar no diagrama que o elemento Estoque tem sua definição completa, com seus atributos e métodos. Implementar em Object Pascal esta classe e sua respectiva interação com um formulário visual é mais trabalhoso, pois se trata de uma operação de movimentação, frente a implementação de classes de negócios que efetuam apenas operações simples de CRUD (acrônimo de Create, Retrieve, Update, Delete) como foi abordado na definição dos elementos Empresa e Endereco. 1.1 O Contexto da classe Estoque A classe Estoque está associada a classe Empresa, pois uma empresa pode possuir vários produtos em estoque do qual queira controlar e manter um histórico de entrada e saída do mesmo. Uma aplicação de controle de estoque pode controlar o estoque de várias empresas, por isso implementamos um cadastro de grupo de empresas, para que possamos cadastrar as empresas que controlarão seus estoques. A cardinalidade entre Empresa e Estoque é de 1..* (um para vários), ou seja, cada empresa controla apenas um único estoque (individualmente), que é constituído por vários produtos, porém o estoque controlado por cada empresa pode ter um saldo diferente de um mesmo produto, sendo que há uma separação lógica do mesmo dentro do banco de dados. Em relação a classe Produto, esta se relaciona com a classe Estoque através da associação com cardinalidade 1..* (um para vários), onde para um determinado produto há a ocorrência de vários registros em um estoque, e o mesmo possui apenas uma referência ao produto. Resumidamente encontramos um produto com identificação única (através do ID) dentro de um estoque definindo o tipo de movimentação (E/S) e a quantidade informada pelo usuário da aplicação. Esta definição ficará mais clara até o final do artigo. O elemento Produto está associado também a um elemento Marca, cardinalidade de *..1, um produto tem uma única marca e uma marca pode pertencer a vários produtos; e pode conter várias medidas de Unidade, cardinalidade de *..*, um produto pode conter várias unidades de medida e as unidades de medidas pertencem a vários produtos. Vejamos em mais detalhes o que a classe Estoque possui em sua definição, detalhando as assinaturas dos métodos implementados: 1) public Validar(): boolean – efetua a validação do estado do objeto instanciado em memória, tornando-o consistente; 2) public Movimentacao(): boolean – a movimentação de entrada e saída de produtos de uma determinada empresa no estoque é realizado através da implementação deste método, que é também o principal método da classe; public ConsultarSaldo(): Double – consulta e retorna o saldo atual de um produto pertencente ao estoque de determinada empresa (saldo entrada – saldo saída); public
  3. 3. getObject(Id: String): boolean – retorna a movimentação do estoque de um produto através de seu ID (código); public getObjects(): boolean – toda a movimentação efetuada no estoque de uma empresa é retornado; public getObjects(Id: String): boolean – através deste método é exibido na Grid do formulário visual (FrmControleEstoque) o histórico de movimentação de um produto especificado pelo argumento ID (código). Figura 1 – Diagrama de Classe Simplificado. Domínio: Controle de Estoque. 2. Objetos de negócio e formulários: Divisão de responsabilidades em duas camadas 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. Adotando o exemplo criado anteriormente na segunda parte da série, continuaremos a implementar o aplicação de controle de estoque. Para quem irá começar a acompanhar o desenvolvimento da aplicação deste ponto, o código-fonte de exemplo pode ser encontrado em http://activedelphi.com.br/downloads/orientacao_objetos.rar. A IDE Delphi 7 Update2 está sendo utilizado no desenvolvimento, pois o objetivo desta série sobre Orientação a Objetos e seus conceitos adjacentes está voltado a implementação do modelo conceitual e não focado na utilização de componentes específicos de software. Para realizar a persistência de dados estamos utilizando o SGBD objeto-relacional PostgreSQL 8.3, que pode ser encontrado em http://www.postgresql.org.
  4. 4. Com o Delphi aberto, procure pelo projeto “ControleEstoque”, que foi salvo anteriormente no diretório “d:projetosoocontrole_estoque” (ou no diretório de sua preferência), abra-o e o mesmo será exibido dentro da IDE. O menu principal da aplicação fora definido anteriormente, restando apenas criar os formulários dos itens de menu. Iremos implementar apenas o formulário de Cadastro de Produto e o de Movimentação de Estoque, ficando a criação do restante dos cadastros sugeridos a critério do leitor, pois ao termino deste artigo todos os princípios básicos de construção de formulários e sua interação com objetos de negócios (classes) definidos no escopo da aplicação serão abordados. 2.1 Classes de negócio Seguindo a mesma linha de desenvolvimento, comecemos com a implementação das classes de negócio, mais especificamente com a classe concreta Unidade; a responsabilidade dessa classe é conter dados relacionados com as unidades de medidas utilizadas pelos produtos. Adicione uma nova unit ao projeto através do Menu File – New – Unit, salve-a no diretório “classes” como clUnidade.pas. No bloco interface/implementation da unit declare/implemente a classe conforme abaixo: unit clUnidade; interface type TUnidade = class(TObject) private // métodos privados // métodos acessores suprimidos. getters / setters. function Insert(): Boolean; function Update(): Boolean; protected // declaração de atributos _codigo: String; _descricao: String; _sigla: String; public // declaração das propriedades da classe, encapsulamento de atributos property Codigo: String read getCodigo write setCodigo; property Descricao: String read getDescricao write setDescricao; property Sigla: String read getSigla write setSigla; // declaração de métodos públicos function Validar(): Boolean; function Merge(): Boolean; function Delete(): Boolean; function getObject(Id: String): Boolean; function getObjects(): Boolean; function getField(campo: String; coluna: String): String; end; const TABLENAME = 'UNIDADE';
  5. 5. implementation uses clUtil, dmConexao, SysUtils, Dialogs; { TUnidade } // implementação suprimida, para não estender muito o artigo end. Depois da definição e implementação da classe acima, precisamos agora implementar a classe Marca que é responsável por conter dados relacionados as marcas dos produtos comercializados por uma empresa do ramo atacadista/varejista. Adicione uma nova unit ao projeto através do Menu File – New – Unit, salve-a no diretório “classes” como clMarca.pas. No bloco interface da unit declare a classe conforme abaixo: unit clMarca; interface type TMarca = class(TObject) private // métodos privados // métodos acessores suprimidos. getters / setters. function Insert(): Boolean; function Update(): Boolean; protected // declaração de atributos _codigo: String; _descricao: String; public // declaração das propriedades da classe, encapsulamento de atributos property Codigo: String read getCodigo write setCodigo; property Descricao: String read getDescricao write setDescricao; // declaração de métodos públicos function Validar(): Boolean; function Merge(): Boolean; function Delete(): Boolean; function getObject(Id: String): Boolean; function getObjects(): Boolean; function getField(campo: String; coluna: String): String; end; const TABLENAME = 'MARCA'; implementation uses clUtil, dmConexao, SysUtils, Dialogs;
  6. 6. { TMarca } // implementação suprimida, para não estender muito o artigo end. Implementamos as classes Unidade e Marca a princípio pois as mesmas estão associadas com a classe Produto. Na notação UML identificada na figura 1, esta associação possui a definição de navegabilidade, ou seja, a classe Produto contém referência a objetos do tipo Unidade e Marca. Logo abaixo na implementação da classe Produto será possível visualizar atributos declarados com os tipos definidos por nós, e no método construtor da classe a responsabilidade em instanciar os objetos referenciados. É denotado aqui a interação entre os objetos de negócio, compondo um rico ambiente onde as diversas unidades de software (objetos), que inter-relacionadas formam o escopo de uma aplicação, definindo a divisão de responsabilidades em classes de negócios, resultando em uma coesão satisfatória e uma granularidade razoável. Adicione uma nova unit ao projeto através do Menu File – New – Unit, salve-a no diretório “classes” como clProduto.pas. Nesta unit declaramos a classe conforme abaixo: unit clProduto; interface // utilização das units declaradas anteriormente, classes de negócios uses clUnidade, clMarca; type TProduto = class(TObject) private // métodos privados // métodos acessores suprimidos. getters / setters. function Insert(): Boolean; function Update(): Boolean; protected // declaração de atributos _codigo: String; _codigoPrincipal: String; _descricao: String; _descReduzido: String; _status: Boolean; _unidade: TUnidade; // _marca: TMarca; // // categoria... public // método construtor definido por nós constructor Create; // declaração das propriedades da classe, encapsulamento de atributos property Codigo: String read getCodigo write setCodigo; property CodigoPrincipal: String read getCodigoPrinc write setCodigoPrinc; property Descricao: String read getDescricao write setDescricao;
  7. 7. property DescReduzido: String read getDescReduzido write setDescReduzido; property Status: Boolean read getStatus write setStatus; property Unidade: TUnidade read getUnidade write setUnidade; property Marca: TMarca read getMarca write setMarca; // na linha acima antes do ponto e virgula (setStatus) pressione Ctrl + Shift + C // para gerar os métodos acessores getter e setter automaticamente // declaração de métodos públicos function Validar(): Boolean; function Merge(): Boolean; function Delete(): Boolean; function getObject(Id: String): Boolean; function getObjects(): Boolean; function getField(campo: String; coluna: String): String; end; const TABLENAME = 'PRODUTO'; implementation uses clUtil, dmConexao, SysUtils, Dialogs; { TProduto } // método construtor constructor TProduto.Create; begin _unidade := TUnidade.Create; // instanciação do objeto Unidade _marca := TMarca.Create; // instanciação do objeto Marca end; // implementação suprimida, para não estender muito o artigo end. Através desta série de artigos sobre orientação a objetos no Delphi, adotamos como exemplo prático o desenvolvimento de uma aplicação de controle de estoque com o intuito de contemplar a maioria dos conceitos apresentados teoricamente na primeira parte da série. E neste momento será definido a classe principal de nosso projeto, ou seja, a classe Estoque que é responsável por controlar o estoque dos produtos de uma determinada empresa através das operações de entrada e saída. Visualizando a modelagem da figura 1, fica claro que o elemento Estoque está no cerne do diagrama de classes, pois está relacionado com a classe Produto e Empresa, que conseqüentemente tem uma rica interação com outros elementos do escopo. É interessante comentar sobre a associação e navegabilidade entre a classe Estoque-Produto e Estoque-Empresa, onde em um estoque efetuamos operações de entrada/saída de produtos definindo a quantidade e valor de custo do mesmo, sobre o estoque de uma determinada empresa em particular. A descrição sobre a navegabilidade pode ser sintetizada através da notação de cardinalidade citado logo no início deste artigo.
  8. 8. Adicione uma nova unit ao projeto através do Menu File – New – Unit, salve-a no diretório “classes” como clEstoque.pas. Como esta classe de negócio é o foco principal da aplicação, será apresentado o código-fonte completo da mesma (exceto os métodos acessores – get/set). Nesta unit declaramos a classe conforme abaixo: unit clEstoque; interface // utilização das units declaradas anteriormente, classes de negócios uses clProduto, clEmpresa; type TEstoque = class(TObject) private // métodos privados // métodos acessores suprimidos. getters / setters. protected // declaração de atributos _codigo: String; _data: TDateTime; _hora: TDateTime; _documento: String; _saldo: Double; _vlrCusto: Currency; _tipoMov: String; _quantidade: Double; _flag: Boolean; _produto: TProduto; _empresa: TEmpresa; public // método construtor definido por nós constructor create; // declaração das propriedades da classe, encapsulamento de atributos property Codigo: String read getCodigo write setCodigo; property Data: TDateTime read getData write setData; property Hora: TDateTime read getHora write setHora; property Documento: String read getDocumento write setDocumento; property Saldo: Double read getSaldo write setSaldo; property VlrCusto: Currency read getVlrCusto write setVlrCusto; property TipoMov: String read getTipoMov write setTipoMov; property Quantidade: Double read getQuantidade write setQuantidade; property Flag: Boolean read getFlag write setFlag; property Produto: TProduto read getProduto write setProduto; property Empresa: TEmpresa read getEmpresa write setEmpresa; // declaração de métodos públicos function Validar(): Boolean; function Movimentacao(): Boolean; function ConsultarSaldo(): Double; function getObject(Id: String): Boolean;
  9. 9. function getObjects: Boolean; overload; function getObjects(Id: String): Boolean; overload; end; const TABLENAME = 'ESTOQUE'; implementation uses SysUtils, dmConexao, Dialogs, clUtil, DB, ZAbstractRODataset; { TEstoque } // método construtor constructor TEstoque.create; begin _produto := TProduto.Create; // instanciação do objeto Produto _empresa := TEmpresa.Create; // instanciação do objeto Empresa end; // implementação suprimida dos métodos acessores (get/set), para não estender muito o artigo // todos os métodos contendo regras de negócios estão relacionados abaixo function TEstoque.ConsultarSaldo(): Double; begin Try Result := 0; with Conexao.QryGetObject do begin Close; SQL.Clear; SQL.Text := 'SELECT SUM(EST_QUANTIDADE) - COALESCE((SELECT SUM(EST_QUANTIDADE) AS EST_SALDO FROM '+ TABLENAME + ' WHERE PRO_CODIGO =:PRODUTO AND EMP_CODIGO =:EMPRESA AND EST_TIPO_MOV = '''+'S'+'''), 0) AS EST_SALDO '+ ' FROM '+ TABLENAME + ' WHERE PRO_CODIGO =:PRODUTO AND EMP_CODIGO =:EMPRESA AND EST_TIPO_MOV = '''+'E'+''' '; ParamByName('PRODUTO').AsString := Self.Produto.Codigo; ParamByName('EMPRESA').AsString := Self.Empresa.Codigo; Open; First; end; if Conexao.QryGetObject.RecordCount > 0 then Result := Conexao.QryGetObject.FieldByName('EST_SALDO').AsFloat; Except on E : Exception do ShowMessage('Classe: '+ e.ClassName + chr(13) + 'Mensagem: '+ e.Message); end; end; function TEstoque.Movimentacao: Boolean; begin // Movimentação de Estoque
  10. 10. Try Result := False; with Conexao.QryCRUD do begin Close; SQL.Clear; SQL.Text := 'INSERT INTO '+ TABLENAME +' (PRO_CODIGO, EMP_CODIGO, EST_DATA, EST_HORA, EST_DOCUMENTO, EST_SALDO, '+ ' EST_VLR_CUSTO, EST_TIPO_MOV, EST_QUANTIDADE) '+ ' VALUES (:PRODUTO, :EMPRESA, :DATA, :HORA, :DOCUMENTO, :SALDO, :VLR_CUSTO, :TIPO_MOV, :QUANTIDADE)'; ParamByName('PRODUTO').AsString := Self.Produto.Codigo; ParamByName('EMPRESA').AsString := Self.Empresa.Codigo; ParamByName('DATA').AsDateTime := Self.Data; ParamByName('HORA').AsDateTime := Now; // hora atual ParamByName('DOCUMENTO').AsString := Self.Documento; // verificando o tipo de movimento if Self.TipoMov = 'E' then ParamByName('SALDO').AsFloat := (Self.ConsultarSaldo + Self.Quantidade) else ParamByName('SALDO').AsFloat := (Self.ConsultarSaldo - Self.Quantidade); ParamByName('VLR_CUSTO').AsCurrency := Self.VlrCusto; ParamByName('TIPO_MOV').AsString := Self.TipoMov; ParamByName('QUANTIDADE').AsFloat := Self.Quantidade; ExecSQL; end; Result := True; Except on E : Exception do ShowMessage('Classe: '+ e.ClassName + chr(13) + 'Mensagem: '+ e.Message); end; end; function TEstoque.getObjects: Boolean; begin Try Result := False; with Conexao.QryGetObject do begin Close; SQL.Clear; SQL.Text := 'SELECT * FROM '+ TABLENAME +' ORDER BY EST_CODIGO'; Open; First; end; if Conexao.QryGetObject.RecordCount > 0 then Result := True; Except on E : Exception do ShowMessage('Classe: '+ e.ClassName + chr(13) + 'Mensagem: '+ e.Message);
  11. 11. end; end; // pegar movimentação de estoque pelo ID (PRODUTO) function TEstoque.getObject(Id: String): Boolean; begin try Result := False; if TUtil.Empty(Id) then Exit; with Conexao.QryGetObject do begin Close; SQL.Clear; SQL.Text := 'SELECT * FROM '+ TABLENAME +' WHERE PRO_CODIGO =:CODIGO AND EMP_CODIGO =:EMPRESA'; ParamByName('CODIGO').AsString := Id; ParamByName('EMPRESA').AsString := Self.Empresa.Codigo; Open; First; end; if Conexao.QryGetObject.RecordCount > 0 then begin Self.Codigo := Conexao.QryGetObject.FieldByName('EST_CODIGO').AsString; Self.Produto.Codigo := Conexao.QryGetObject.FieldByName('PRO_CODIGO').AsString; Self.Data := Conexao.QryGetObject.FieldByName('EST_DATA').AsDateTime; Self.Hora := Conexao.QryGetObject.FieldByName('EST_HORA').AsDateTime; Self.Documento := Conexao.QryGetObject.FieldByName('EST_DOCUMENTO').AsString; Self.Saldo := Conexao.QryGetObject.FieldByName('EST_SALDO').AsFloat; Self.VlrCusto := Conexao.QryGetObject.FieldByName('EST_VLR_CUSTO').AsCurrency; Self.TipoMov := Conexao.QryGetObject.FieldByName('EST_TIPO_MOV').AsString; Self.Quantidade := Conexao.QryGetObject.FieldByName('EST_QUANTIDADE').AsFloat; Self.Flag := Conexao.QryGetObject.FieldByName('EST_FLAG').AsBoolean; Result := True; end; Except on E : Exception do ShowMessage('Classe: '+ e.ClassName + chr(13) + 'Mensagem: '+ e.Message); end; end; function TEstoque.Validar: Boolean; begin // validação de atributos if Length(DateToStr(_data)) <> 10 then begin ShowMessage('Data Inválida!'); Result := false; Exit; end else if _quantidade = 0 then begin ShowMessage('Quantidade Inválida!'); Result := False; Exit; end;
  12. 12. Result := True; end; function TEstoque.getObjects(Id: String): Boolean; begin Try Result := False; with Conexao.QryEstoque do begin Close; SQL.Clear; SQL.Text := 'SELECT * FROM '+ TABLENAME +' WHERE PRO_CODIGO =:CODIGO AND EMP_CODIGO =:EMPRESA ORDER BY EST_CODIGO'; ParamByName('CODIGO').AsString := Id; ParamByName('EMPRESA').AsString := Self.Empresa.Codigo; Open; First; end; if Conexao.QryGetObject.RecordCount > 0 then begin Self.getObject(Id); Result := True; end; Except on E : Exception do ShowMessage('Classe: '+ e.ClassName + chr(13) + 'Mensagem: '+ e.Message); end; end; end. Agora que temos o diagrama de classes representado pela figura 1 implementado totalmente em Object Pascal (exceto a classe Categoria), vamos construir o formulário que irá persistir os objetos do tipo Produto e outro que efetue o controle de estoque, através da movimentação de produtos operando a entrada e saída de produtos, atualizando seu saldo corrente. 2.2 A camada de visualização de dados: O formulário Utilizando o conceito RAD (Rapid Application Development - Desenvolvimento Rápido de Aplicativos) da IDE Delphi construímos formulários visuais com rapidez e facilidade, obtemos uma velocidade ainda maior no desenvolvimento pois a implementação de toda a lógica da aplicação já está definida. Com isso os formulários passam a atuar definitivamente apenas como instrumentos de entrada e visualização de dados, tendo sua responsabilidade resumida a isto. Não é necessário especificar todos os componentes visuais e suas respectivas propriedades, estou assumindo que a maioria dos leitores possui plenos conhecimentos em construção de formulários visuais utilizando o Delphi. Para a construção do formulário de Cadastro de Produtos tomemos como base o layout sugerido pela figura 2. E para o formulário principal da aplicação, o de Controle de Estoque adotemos o layout proposto pela figura 3.
  13. 13. Figura 2 – Formulário de Cadastro de Produto. No formulário da figura 2, podemos utilizar a classe Produto declarando uma variável do tipo TProduto no bloco interface/var da unit. Todos os dados digitados no formulário são atribuídos as propriedades da classe Produto, alterando o estado do objeto instanciado, que é então persistido no SGBD. Toda a interação efetuada com o formulário ocorre através de eventos, que quando disparados executam algum procedimento dentro da aplicação. Para exemplificar o que acabados de descrever, ao clicarmos sobre o botão Salvar (ícone disquete), é invocado o evento onClick sppSalvarClick(Sender: TObject) que contempla a seguinte declaração: procedure TFrmCadastroProduto.sppSalvarClick(Sender: TObject); var Operacao: String; begin Operacao := 'U'; // atribuindo os valores digitados no formulário as propriedades do objeto Produto Produto.Codigo := edtCodigo.Text; Produto.CodigoPrincipal := edtCodigoPrinc.Text; Produto.Descricao := edtDescricao.Text; Produto.DescReduzido := edtDescRed.Text; if cmbStatus.ItemIndex = 0 then Produto.Status := false else Produto.Status := true; if TUtil.Empty(Produto.Codigo) then Operacao := 'I';
  14. 14. if Produto.Validar() then if Produto.Merge() then begin if Operacao = 'I' then ShowMessage('Registro Gravado com Sucesso!') else ShowMessage('Registro Atualizado com Sucesso!'); Conexao.QryProduto.Close; Conexao.QryProduto.Open; sppCancelarClick(Self); end; end; Antes mesmo da chamada do método Merge() que é responsável por persistir o objeto no banco de dados, as propriedades do objeto são alteradas através da atribuição dos valores digitados no formulário. Notamos que o formulário apenas “repassa” os valores ao objeto e o mesmo se encarrega de validar e persistir os dados efetivamente. O que acabamos de descrever aqui é um modelo de divisão de responsabilidade onde cada unidade de software é responsável por determinada atividade. Temos mais dois eventos declarados no formulário da figura 2 que serão abordados – o procedimento privado onClick sppCancelarClick(Sender: TObject) que limpa os campos do formulário, destrói e instancia novamente o objeto do tipo Produto, renovando seu estado. Quando clicamos no botão cancelar (ícone X), o seguinte procedimento é executado: procedure TFrmCadastroProduto.sppCancelarClick(Sender: TObject); begin TUtil.LimparFields(FrmCadastroProduto); Produto := nil; Produto := TProduto.Create; edtDescricao.SetFocus; end; O segundo evento citado no parágrafo acima é o procedimento onClick sppExcluirClick(Sender: TObject) que deleta um objeto Produto da tabela relacional se a propriedade código do objeto instanciado em memória estiver com valor definido e for consistente. Quando clicamos no botão excluir (ícone lixeira), o seguinte evento é disparado: procedure TFrmCadastroProduto.sppExcluirClick(Sender: TObject); begin if TUtil.Empty(Produto.Codigo) then Exit; if MessageBox(Application.Handle, 'Deseja Realmente Excluir o Registro?', 'Controle Estoque', MB_ICONQUESTION + MB_YESNO + MB_DEFBUTTON2) = ID_NO then Exit; if TUtil.NotEmpty(Produto.Codigo) then begin if NOT Produto.Delete then ShowMessage('Erro ao Excluir o Registro.') else begin TUtil.LimparFields(FrmCadastroProduto);
  15. 15. Conexao.QryProduto.Close; Conexao.QryProduto.Open; end; end; end; Com o cadastro de produto em pleno funcionamento podemos realizar operações CRUD com o mesmo, necessitando a princípio inserir produtos que serão controlados pelo formulário da figura 3, o controle de estoque. Neste formulário selecionamos a empresa da qual estamos controlando o estoque de produtos, cada empresa possui um estoque independente conforme citado anteriormente. No campo “Código do Produto” digitamos o código do produto do qual queiramos pesquisar e visualizar o histórico de movimentação de entrada e saída, assim como seu saldo atual em estoque. Após um produto ser encontrado, seu estado é recuperado do banco de dados e definido na instancia do objeto em memória (objeto Estoque.Produto), o componente visual groupBox de movimentação é exibido, permitindo a movimentação de Entrada(1) ou Saída(2) do produto – informando uma quantidade (número de ponto flutuante positivo), valor monetário de custo (compra), número do documento do qual origina-se o movimento. Ao clicar no tipo de movimento (componente radioGroup) o botão movimentar é ativado e sua propriedade caption é modificada de acordo com o tipo selecionado. Ao clicar no referido botão o seguinte evento é disparado: procedure TFrmControleEstoque.btnMovimentarClick(Sender: TObject); begin if MessageBox(Application.Handle, 'Deseja Realmente Movimentar o Estoque?', 'Controle Estoque', MB_ICONQUESTION + MB_YESNO + MB_DEFBUTTON2) = ID_NO then Exit; case rdgTipo.ItemIndex of 0 : Estoque.TipoMov := 'E'; // entrada 1 : Estoque.TipoMov := 'S'; // saída end; // alterando o estado do objeto Estoque Estoque.Data := StrToDate(mskData.Text); Estoque.Quantidade := StrToFloat(edtQuantidade.Text); Estoque.VlrCusto := StrToFloat(edtValorCusto.Text); Estoque.Documento := edtDocumento.Text; if Estoque.Validar() then if Estoque.Movimentacao() then LimparMovimento else ShowMessage('Problemas ao Efeturar Movimentação no Estoque!'); end;
  16. 16. Figura 3 – Formulário de Controle de Estoque (movimentação). O método Movimentacao(): boolean é o principal método definido no escopo da aplicação, executando a movimentação de estoque, atualizando o saldo em estoque do produto selecionado. Caso tenha alguma dúvida em como a regra está implementada na classe clEstoque.pas verifique novamente a declaração do código da classe definida anteriormente. O formulário da figura 3 pode ser invocado também através do cadastro de produtos, clicando no botão “Verificar Estoque”; se algum produto estivar selecionado, ou seja, sendo visualizado no cadastro de produto, o mesmo será passado por referência ao objeto declarado como public ProdutoRef na unit do formulário de controle de estoque. O evento onClick do botão citado é definido na unit untCadastroProduto.pas como: procedure TFrmCadastroProduto.btnEstoqueClick(Sender: TObject); begin if NOT Assigned(FrmControleEstoque) then FrmControleEstoque := TFrmControleEstoque.Create(Application); // passando o Objeto Produto para o outro formulário (estoque) // em cada formulário encontramos um objeto do tipo produto // sendo possível passar o estado de um objeto de um formulário // ao objeto de outro formulário if TUtil.NotEmpty(Produto.Codigo) then FrmControleEstoque.ProdutoRef := Produto; FrmControleEstoque.ShowModal; end;
  17. 17. O processo de transferir o estado de um objeto instanciado em um formulário a outro é comumente utilizado e de simples utilização. Este procedimento é utilizado também em formulários de pesquisa, que geralmente são invocados por formulários de cadastro. Na segunda parte da série criamos o banco de dados “ActiveDelphi” através da ferramenta gráfica pgAdminIII (acompanha a instalação do SGBD PostgreSQL), execute-a, com ela aberta dê dois cliques no hostname onde o banco de dados fora criado, realize o login, forneça apenas a senha que foi definida ao usuário do banco. No item Banco de Dados, expanda a lista de bancos de dados existentes e procure pelo nome “ActiveDelphi”, selecione-o; através do editor SQL (clicando no ícone SQL) execute o seguinte script SQL DDL (Data Definition Language): -- MARCA CREATE TABLE MARCA( MAR_CODIGO SERIAL NOT NULL, MAR_DESCRICAO VARCHAR(100) NOT NULL ); ALTER TABLE MARCA ADD CONSTRAINT PK_MARCA PRIMARY KEY(MAR_CODIGO); -- POPULANDO A TABELA MARCA INSERT INTO MARCA (MAR_DESCRICAO) VALUES ('DUREX'); INSERT INTO MARCA (MAR_DESCRICAO) VALUES ('EATON'); INSERT INTO MARCA (MAR_DESCRICAO) VALUES ('INDISA'); INSERT INTO MARCA (MAR_DESCRICAO) VALUES ('LUCIFLEX'); INSERT INTO MARCA (MAR_DESCRICAO) VALUES ('PERKINS'); INSERT INTO MARCA (MAR_DESCRICAO) VALUES ('STAHL'); -- UNIDADE CREATE TABLE UNIDADE( UND_CODIGO SERIAL NOT NULL, UND_DESCRICAO VARCHAR(100) NOT NULL, UND_SIGLA VARCHAR(5) UNIQUE NOT NULL ); ALTER TABLE UNIDADE ADD CONSTRAINT PK_UNIDADE PRIMARY KEY(UND_CODIGO); -- POPULANDO A TABELA UNIDADE INSERT INTO UNIDADE (UND_DESCRICAO, UND_SIGLA) VALUES ('CAIXA', 'CXA'); INSERT INTO UNIDADE (UND_DESCRICAO, UND_SIGLA) VALUES ('UNIDADE', 'UND'); INSERT INTO UNIDADE (UND_DESCRICAO, UND_SIGLA) VALUES ('KILO', 'KG'); INSERT INTO UNIDADE (UND_DESCRICAO, UND_SIGLA) VALUES ('METRO', 'MT'); -- PRODUTO CREATE TABLE PRODUTO( PRO_CODIGO SERIAL NOT NULL, PRO_C_PRINCIPAL VARCHAR(20), PRO_STATUS BOOLEAN, PRO_DESCRICAO VARCHAR(200),
  18. 18. PRO_DESC_REDUZIDO VARCHAR(50), MAR_CODIGO INTEGER, UND_CODIGO INTEGER ); ALTER TABLE PRODUTO ADD CONSTRAINT PK_PRODUTO PRIMARY KEY(PRO_CODIGO); ALTER TABLE PRODUTO ADD CONSTRAINT FK_PRODUTO_MARCA FOREIGN KEY(MAR_CODIGO) REFERENCES MARCA(MAR_CODIGO); ALTER TABLE PRODUTO ADD CONSTRAINT FK_PRODUTO_UNIDADE FOREIGN KEY(UND_CODIGO) REFERENCES UNIDADE(UND_CODIGO); -- ESTOQUE CREATE TABLE ESTOQUE( EST_CODIGO SERIAL NOT NULL, PRO_CODIGO INTEGER NOT NULL, -- PRODUTO EMP_CODIGO INTEGER NOT NULL, -- EMPRESA EST_DATA DATE NOT NULL, EST_HORA TIME NOT NULL, EST_DOCUMENTO VARCHAR(20), EST_SALDO NUMERIC(15,5), EST_VLR_CUSTO NUMERIC(15,5), EST_TIPO_MOV CHAR, EST_QUANTIDADE NUMERIC(15,5), EST_FLAG BOOLEAN ); ALTER TABLE ESTOQUE ADD CONSTRAINT PK_ESTOQUE PRIMARY KEY(EST_CODIGO); ALTER TABLE ESTOQUE ADD CONSTRAINT FK_ESTOQUE_PRODUTO FOREIGN KEY(PRO_CODIGO) REFERENCES PRODUTO(PRO_CODIGO); ALTER TABLE ESTOQUE ADD CONSTRAINT FK_ESTOQUE_EMPRESA FOREIGN KEY(EMP_CODIGO) REFERENCES EMPRESA(EMP_CODIGO); Terminamos neste momento a aplicação de controle de estoque, que poderá ser baixada na integra em http://ryanpadilha.com.br/downloads/active_dephi/controle_estoque_parte2.rar. Caso algum leitor tenha alguma sugestão, crítica ou elogio referente ao que foi abordado e implementado aqui, entre em contato com este colunista que vos escreve. 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. 2.3 Justificativa da arquitetura em duas camadas Alguns leitores podem argumentar neste momento: Em aplicações que utilizam o paradigma estruturado, o formulário trata os dados digitados, não repassa informação a nenhum outro lugar, apenas persiste a informação em uma tabela relacional utilizando componentes de acesso direto a banco de dados (os famosos componentes DB<objeto>), a unit do formulário contem toda a lógica, resumindo é muito rápido desenvolver software assim. E com a arquitetura proposta neste artigo temos a divisão de responsabilidades em duas camadas
  19. 19. (model e view), onde a implementação fica separada uma da outra e que no final da um pouco mais de trabalho em implementar. Afinal o que ganhamos com a utilização desse modelo de duas camadas ? Sinceramente este questionamento ocorre com freqüência, a maioria é feita por desenvolvedores originados do modelo estrutural de desenvolvimento. A orientação a objetos é uma evolução conceitual dentro do ramo da engenharia de software que vem se mostrando cada dia mais poderosa e flexível. Porém não é a palavra final tratando-se de desenvolvimento de software, com certeza a evolução caminha e novas tecnologias e conceitos surgem, complementando o modelo anteriormente proposto pelos pesquisadores e engenheiros de software. Retornando a pergunta provavelmente feita por muitos leitores, temos vários conceitos envolvidos neste modelo proposto: 1) O princípio de responsabilidade única (SRP – Single Responsability Principle) – onde cada classe ou até mesmo unit detêm responsabilidade exclusiva dentro do contexto da aplicação, aumentando significativamente a coesão das unidades de software; 2) Arquitetura de software em camadas – a manutenção do software que corresponde em média a 80% do investimento é facilitado pois temos divisão de implementação em camadas, reduzindo o acoplamento, ou seja, tornando as classes/units mais dependentes umas das outras; 3) Nível satisfatório de coesão e acoplamento – através da definição de classes coesas fica mais fácil de manter e estender suas funcionalidade assim como mantemos um conjunto de classes de regras de negócios que são auto-independentes da plataforma, ou seja, podemos ter as mesmas classes de negócios com apenas a camada de visualização (view) distintas – desktop ou intraweb, pois as temos um baixo acoplamento entre as camadas. 3. Conclusão Esta série de três partes sobre o paradigma orientado a objetos tem seu encerramento no fechamento deste artigo, do qual teve como objetivo revisar tudo o que fora visto até então sobre os conceitos teóricos e práticos e reforçar o que ainda não tinha sido abordado. Introduzimos o leitor no “mundo OO” exibindo suas vantagens e desvantagens, as dificuldades encontradas por programadores que desejam iniciar-se neste contexto e as tecnologias adjacentes que auxiliam o desenvolvedor a alcançar a qualidade de software. A teoria apresentada fora implementada na prática, comprovando que é possível sim desenvolver aplicativos robustos e flexíveis, orientados a objeto, utilizando o Object Pascal que é pouco difundido entre os desenvolvedores da comunidade Delphi, pois há uma carência de material de qualidade sobre o assunto. A aplicação de controle de estoque teve sua arquitetura definida em duas camadas (model/view), objetivando uma alta coesão e baixo acoplamento entre unidades de software, porém em uma medida satisfatória de granularidade. Este conceito de duas camadas pode ser substituída pelo padrão de projeto MVC (model/view/controller) melhorando o escopo de controle de estoque adotado. Em um próximo artigo veremos conceitualmente como este padrão de projeto opera e como pode ser implementado na prática sem maiores dificuldades.
  20. 20. Fico contente e gratificado em ter ajudado os leitores a esclarecer suas dúvidas sobre orientação a objetos. A área de Engenharia de Software é ampla e contempla muitos conceitos e práticas das quais serão abordadas em outras oportunidades. Caso algum leitor tenha sugestões de artigos sobre a área citada entre em contato comigo. Será uma honra escrever novos artigos sobre arquitetura e engenharia. Caso tenha alguma dúvida entre em contato comigo. Será um prazer ajudar. Forte Abraço a todos os leitores que acompanharam a série Delphi OO – teórico e prático!

×