orientação a objetos no delphi - controle de estoque (iii)

20
Orientação a Objetos no Delphi: Controle de Estoque Parte Final Ryan Bruno C Padilha [email protected] 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 .

Upload: ryan-padilha

Post on 22-Jun-2015

3.714 views

Category:

Software


7 download

TRANSCRIPT

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

Orientação a Objetos no Delphi: Controle de Estoque – Parte Final

Ryan Bruno C Padilha

[email protected]

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.

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

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

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

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.

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

Com o Delphi aberto, procure pelo projeto “ControleEstoque”, que foi salvo anteriormente no

diretório “d:\projetos\oo\controle_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';

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

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;

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

{ 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;

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

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.

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

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;

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

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

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

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);

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

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;

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

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.

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

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';

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

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);

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

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;

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

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;

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

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),

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

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

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

(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.

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

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!