Software e Engenharia de Software ENGENHARIA DE SOFTWARE - PRESSMAN
UNIVERSIDADE FEDERAL DE SANTA CATARINA de... · “A Engenharia de Software é uma disciplina da...
Transcript of UNIVERSIDADE FEDERAL DE SANTA CATARINA de... · “A Engenharia de Software é uma disciplina da...
UNIVERSIDADE FEDERAL DE SANTA CATARINA
DEPARTAMENTO DE INFORMÁTICA E ESTATÍSTICA
CURSO DE CIÊNCIAS DA COMPUTAÇÃO
USO DE PADRÕES DE ANÁLISE E PADRÕES DE
PROJETO NO DESENVOLVIMENTO DE UMA
APLICAÇÃO DE CONTROLE DE ATACADO
Autor: Igor Tibes Ghisi
Orientadora: Dr.ª Patrícia Vilain
Banca Examinadora: Dr. Ricardo Pereira e Silva
Dr. Ronaldo dos Santos Mello
Palavras-chave: padrões de análise, padrões de projeto
Florianópolis, 10 de março de 2004
Agradecimentos
Agradeço primeiro e mais especialmente à minha orientadora, Prof. Patrícia
Vilain, pela paciência, preocupação e dedicação na orientação do trabalho.
Ao Prof. Ronaldo dos Santos Mello, pela correção e pelas orientações.
Ao Prof. Ricardo Pereira e Silva, pela avaliação criteriosa e imparcial.
Ao Prof. Orestes Estevam Alarcon e à Renata Santos, que cederam
equipamentos para implementação e apresentação do trabalho.
A minha família, Ivo, Arlete e Alice, pela compreensão e força que me deram.
Aos meus amigos, colegas de faculdade e colegas de trabalho pelo apoio e pelas
informações que me ajudaram a realizar o trabalho.
RESUMO
Este trabalho tem como objetivo desenvolver um estudo sobre o uso de padrões de
análise e padrões de projeto no processo de produção de software. Foram descritos
alguns padrões de análise e de projeto e fez-se um mapeamento dos padrões de análise
para padrões de projeto, quando pertinente. Para aplicar os padrões estudados e o
mapeamento definido desenvolveu-se uma aplicação de Controle de Atacado, utilizando
alguns padrões descritos. Também foi discutido o impacto destes na qualidade do
software desenvolvido.
Palavras-chave: padrões de análise, padrões de projeto.
ABSTRACT
The goal of this work is develop a study about the use of analysis patterns and design
patterns on the software development. Some analysis and design patterns were
described and a mapping of some analysis patterns to design patterns was proposed. A
software for Inventory Control was built applying some patterns and the mapping
defined. Also, the impact of patterns application in the quality of developed software
was discussed.
Key words: analisys patterns, design patterns.
SUMÁRIO
RESUMO
ABSTRACT
SUMÁRIO
ÍNDICE DE FIGURAS
1 INTRODUÇÃO ...................................................................................................... 10
1.1 CARACTERIZAÇÃO DO TEMA E MOTIVAÇÃO....................................................... 10
1.2 OBJETIVOS ............................................................................................................ 11
1.2.1 OBJETIVO GERAL ................................................................................................. 11
1.2.2 OBJETIVOS ESPECÍFICOS ...................................................................................... 11
2 RATIONAL UNIFIED PROCCESS .................................................................... 12
2.1 ESTRUTURA ........................................................................................................... 13
2.2 WORKFLOW MODELAGEM DE NEGÓCIOS............................................................. 16
2.3 WORKFLOW DE REQUISITOS.................................................................................. 16
2.4 WORKFLOW DE ANÁLISE E PROJETO .................................................................... 17
3 PADRÕES DE ANÁLISE...................................................................................... 19
3.1 RESPONSABILIDADE .............................................................................................. 19
3.1.1 ENTIDADE............................................................................................................ 19
3.1.2 HIERARQUIA DE ORGANIZAÇÕES......................................................................... 21
3.1.3 ESTRUTURA DE ORGANIZAÇÕES .......................................................................... 22
3.1.4 RESPONSABILIDADE ............................................................................................ 24
3.1.5 NÍVEL DE INFORMAÇÃO DE RESPONSABILIDADE ................................................. 25
3.2 ESTOQUE E CONTABILIDADE................................................................................ 27
3.2.1 PADRÃO CONTA .................................................................................................. 27
3.2.2 TRANSAÇÕES....................................................................................................... 28
3.2.3 TRANSAÇÕES MÚLTIPLAS.................................................................................... 29
3.2.4 CONTA RESUMIDA............................................................................................... 30
3.2.5 CONTA GATILHO ................................................................................................. 31
3.2.6 REGRA DE LANÇAMENTO .................................................................................... 32
3.2.7 REGRAS DE LANÇAMENTOS PARA VÁRIAS CONTAS.............................................. 33
3.3 COMÉRCIO ............................................................................................................ 35
3.3.1 CONTRATO .......................................................................................................... 36
3.3.2 PORTFÓLIO .......................................................................................................... 38
4 PADRÕES DE PROJETO..................................................................................... 41
4.1 SINGLETON............................................................................................................. 41
4.2 FACTORY METHOD................................................................................................. 43
4.2.1 ESTRUTURA ......................................................................................................... 45
4.2.2 IMPLEMENTAÇÃO ................................................................................................ 46
4.3 PROXY .................................................................................................................... 46
4.3.1 IMPLEMENTAÇÃO ................................................................................................ 47
4.3.2 ESTRUTURA ......................................................................................................... 48
4.4 FACADE.................................................................................................................. 49
4.4.1 IMPLEMENTAÇÃO ................................................................................................ 51
4.4.2 ESTRUTURA ......................................................................................................... 52
4.5 STATE ..................................................................................................................... 53
4.5.1 ESTRUTURA ......................................................................................................... 54
4.5.2 IMPLEMENTAÇÃO ................................................................................................ 54
4.6 COMPOSITE ............................................................................................................ 56
4.6.1 ESTRUTURA ......................................................................................................... 56
4.6.2 IMPLEMENTAÇÃO ................................................................................................ 57
4.7 STRATEGY .............................................................................................................. 58
4.7.1 ESTRUTURA ......................................................................................................... 58
4.7.2 IMPLEMENTAÇÃO ................................................................................................ 58
5 MAPEAMENTO DE PADRÕES DE ANÁLISE PARA PADRÕES DE
PROJETO ..................................................................................................................... 60
5.1 PADRÃO DE ANÁLISE TRANSAÇÕES E PADRÃO DE PROJETO PROXY................... 60
5.2 PADRÃO DE ANÁLISE CONTA RESUMIDA E PADRÃO DE PROJETO COMPOSITE .. 61
5.3 PADRÃO DE ANÁLISE PORTFÓLIO E PADRÃO DE PROJETO STRATEGY ............... 64
5.3.1 USO DO SINGLETON.............................................................................................. 66
5.4 PADRÃO DE ANÁLISE REGRA DE LANÇAMENTO E PADRÃO STRATEGY............... 67
6 DESENVOLVIMENTO......................................................................................... 69
6.1 LEVANTAMENTO DE REQUISITOS......................................................................... 69
6.1.1 DESCRIÇÃO DO DOMÍNIO DA APLICAÇÃO............................................................ 69
6.1.2 CASOS DE USO..................................................................................................... 70
6.2 ANÁLISE ................................................................................................................ 80
6.2.1 MODELO CONCEITUAL ........................................................................................ 80
6.3 PROJETO................................................................................................................ 88
6.3.1 LINGUAGEM E BANCO DE DADOS ........................................................................ 88
6.3.2 DIAGRAMAS DE SEQUÊNCIA ................................................................................ 89
6.3.2.1 Consignar......................................................................................................... 90
6.3.2.2 Vender ............................................................................................................. 91
6.3.3 DIAGRAMA DE CLASSES ...................................................................................... 92
6.3.3.1 Contratos.......................................................................................................... 92
6.3.3.2 Conta Caixa ..................................................................................................... 94
6.3.3.3 Camada de Persistência ................................................................................... 96
6.4 IMPLEMENTAÇÃO.................................................................................................. 98
6.4.1 MÉTODO VENDER................................................................................................ 98
6.4.2 MÉTODO OBTERVENDAS ..................................................................................... 99
6.4.3 CAMADA DE PERSISTÊNCIA ............................................................................... 100
6.4.4 PROTÓTIPO ........................................................................................................ 101
6.4.4.1 Funcionamento do Protótipo ......................................................................... 102
7 CONCLUSÃO....................................................................................................... 104
7.1 FATORES DE QUALIDADE ALCANÇADOS ............................................................ 104
7.1.1 ESTENDIBILIDADE ............................................................................................. 104
7.1.2 DESEMPENHO .................................................................................................... 105
7.1.3 REUSABILIDADE ................................................................................................ 105
7.1.4 PORTABILIDADE ................................................................................................ 105
7.2 CONSIDERAÇÕES FINAIS..................................................................................... 106
BIBLIOGRAFIA CONSULTADA ........................................................................... 107
ANEXOS ..................................................................................................................... 109
ANEXO I: ARTIGO ..................................................................................................... 110
ANEXO II: DIAGRAMA DE CLASSES (CAMADA DE APLICAÇÃO).............................. 112
ANEXO III: CÓDIGO DA APLICAÇÃO ........................................................................ 114
ÍNDICE DE FIGURAS
Figura 3.1 – Padrão Entidade ......................................................................................... 20
Figura 3.2 – Modelo de hierarquia simples .................................................................... 21
Figura 3.3 – Modelo de hierarquia com restrições ......................................................... 22
Figura 3.4 – Estrutura de Organização ........................................................................... 23
Figura 3.5 – Hierarquia utilizando padrão Entidade....................................................... 24
Figura 3.6 – Divisão em camadas................................................................................... 26
Figura 3.7 – Conta e Lançamento................................................................................... 27
Figura 3.8 - Transação.................................................................................................... 29
Figura 3.9 – Transação Múltipla .................................................................................... 30
Figura 3.10 – Conta Resumida ....................................................................................... 30
Figura 3.11 – Regra de Lançamento............................................................................... 32
Figura 3.12 – Metodo de Cálculo para saida .................................................................. 33
Figura 3.13 – Conta e Tipo de Conta.............................................................................. 34
Figura 3.14 – Localizador de Conta ............................................................................... 35
Figura 3.15 – Contrato com especificação ..................................................................... 36
Figura 3.16 – Contrato com associações ........................................................................ 37
Figura 3.17 - Contrato .................................................................................................... 37
Figura 3.18 - Portfólio .................................................................................................... 38
Figura 3.19 – Seleção de Contratos por método booleano............................................. 39
Figura 3.20 – Seleção de Contratos por comparação com Seletor ................................. 40
Figura 4.1 – Estrutura do Padrão Singleton.................................................................... 42
Figura 4.2 – Estrutura do Factory Method ..................................................................... 45
Figura 4.3 – Exemplo do Proxy...................................................................................... 47
Figura 4.4 – Etrutura do Proxy ....................................................................................... 49
Figura 4.5 – Sem o uso do Facade ................................................................................. 50
Figura 4.6 – Com o uso do Facade ................................................................................. 51
Figura 4.7 – Estrutura do Facade ................................................................................... 52
Figura 4.8 – Padrão State................................................................................................ 53
Figura 4.9 – Estrutura do State ....................................................................................... 54
Figura 4.10 – Estrutura do Composite ............................................................................ 56
Figura 4.11 – Estrutura do padrão Strategy.................................................................... 58
Figura 4.12 – Estrutura do Strategy................................................................................ 59
Figura 5.1 – Estrutura do Proxy ..................................................................................... 61
Figura 5.2 – Padrão de Análise Conta Resumida ........................................................... 62
Figura 5.3 - Padrão de Projeto Composite...................................................................... 62
Figura 5.4 – Diagrama de classe do mapeamento .......................................................... 63
Figura 5.5 – Seleção de contrato por método ................................................................. 64
Figura 5.6 – Estrutura da implementação....................................................................... 65
Figura 5.7 – Portfólio mapeado para Singleton e Strategy ............................................. 66
Figura 5.8 – Padrão de análise Regra de Lançamento.................................................... 67
Figura 5.9 – Mapeamento do Padrão Regra de Lançamento para o Strategy ................ 67
Figura 6.1 – Modelo baseado no padrão Entidade ......................................................... 81
Figura 6.2 – Modelo sem conceito de Contrato.............................................................. 82
Figura 6.3 – Conceito de Contrato ................................................................................. 83
Figura 6.4 – Modelo com conceito de Contrato ............................................................. 84
Figura 6.5 – Conta Caixa, Receita e Despesa................................................................. 85
Figura 6.6 – Lançamentos nos Caixas ............................................................................ 86
Figura 6.7 – Modelo conceitual da aplicacao................................................................. 87
Figura 6.8 – Diagrama de Sequência Consignar ............................................................ 90
Figura 6.9 – Diagrama de Sequência Vender ................................................................. 91
Figura 6.10 – Contrato com Proxy ................................................................................. 93
Figura 6.11 – Heranças de contrato com Proxy.............................................................. 94
Figura 6.12 – Conta Resumida projetada para Caixas.................................................... 95
Figura 6.13 – Camada de Persistencia............................................................................ 97
Figura 6.14 - Protótipo da Aplicação............................................................................ 102
Figura 6.15 – Consultando o estoque ........................................................................... 103
Figura 6.16 – Realizando a venda ................................................................................ 103
Figura 6.17 – Consultando a venda realizada............................................................... 103
1 INTRODUÇÃO
1.1 Caracterização do Tema e Motivação
A capacidade da indústria de software dos dias atuais de desenvolver sistemas de
grande complexidade se deve à implementação de um tratamento sistemático e
controlado à etapa de desenvolvimento da solução computacional, o qual denomina-se
engenharia de software.
“A Engenharia de Software é uma disciplina da engenharia que se ocupa de
todos os aspectos da produção de software, desde os estágios iniciais de especificação
do sistema até a manutenção desse sistema” (SOMMERVILLE, 2003).
A engenharia de software surgiu nos anos 70 e hoje se tornou imprescindível
para qualquer softwarehouse que deseja, além de desenvolver aplicações maiores e mais
complexas, também garantir produtos de qualidade.
Dentre as ferramentas de engenharia de software que tornam possível a
complexidade dos sistemas atuais, estão os padrões. Os padrões são soluções utilizadas
no processo de desenvolvimento de uma aplicação que podem ser utilizadas novamente
em outro processo, mesmo que o domínio do problema seja diferente.
Dessa maneira, não é necessário dispender esforço na resolução de um problema
que já foi solucionado no passado.
“Cada padrão descreve um problema que ocorre mais de uma vez em um
certo contexto, e descreve o núcleo da solução para este problema de uma
forma que você pode usar essa solução um milhão de vezes, sem ter que
elabora-la novamente” (ALEXANDER, 1977 apud GAMMA, 1994)
Padrões são usados em vários campos de trabalho como construção civil e
gerenciamento empresarial. Foi adotado no desenvolvimento de software a partir dos
anos 90. Nos últimos anos os padrões têm sido bastante discutidos.
11
“Padrões não são inventados, mas sim descobertos” (FOWLER, 1997). Isso
porque a maioria dos padrões nasce de um modelo feito para um domínio particular, e o
desenvolvedor nota que o modelo pode ser usado em outros domínios. Assim o modelo
se torna um padrão.
Neste trabalho realizou-se o estudo de padrões para duas fases do
desenvolvimento de software. Padrões para análise e padrões para projeto. Analisou-se,
também, como ocorre a utilização destes padrões no desenvolvimento da aplicação. E
quais os benefícios desta utilização.
1.2 Objetivos
1.2.1 Objetivo geral
Ao fim do trabalho deve-se ter uma descrição de vários padrões de análise e
padrões de projeto e, com o estudo de cada um, ver quais se adequam ao domínio da
aplicação e que facilidades estes trazem para desenvolvimento de uma aplicação de
controle de atacado.
1.2.2 Objetivos específicos
• Fazer um estudo de alguns padrões de análise.
• Fazer um estudo de alguns padrões de projetos.
• Fazer um mapeamento dos padrões de análise para os padrões de projeto.
• Desenvolver um modelo (análise e projeto) para o domínio de controle de
atacado, utilizando, sempre que pertinente, os padrões de análise e padrões de
projeto estudados.
• Implementar um protótipo da aplicação.
2 RATIONAL UNIFIED PROCCESS
Dentro de um processo de desenvolvimento, o Rational Unified Proccess (RUP)
(KRUCHTEN, 2000) disponibiliza uma abordagem disciplinada para a atribuição de
tarefas e responsabilidades e assegura a produção de um software de alto padrão.
De acordo com Booch (BOOCH apud KRUCHTEN, 2000) um processo de
desenvolvimento de software deve possuir quatro funções:
1. Ordenar as atividades da equipe de desenvolvimento.
2. Especificar quais artefatos de software deverão ser desenvolvidos e quando
deverão ser desenvolvidos.
3. Definir as tarefas que serão individuais e tarefas executadas em conjunto
pela equipe.
4. Sugerir um critério de monitoração e avaliação dos produtos e atividades do
projeto.
“O processo de desenvolvimento de software sistematiza o desenvolvimento de
aplicações complexas e possibilita produzir um software de qualidade de uma maneira
previsível. Com isso, os custos de produção são reduzidos e a produtividade é elevada”
(KRUCHTEN, 2000)
Por ser um moderno processo de engenharia de software, provendo ferramentas
eficientes para assegurar a produção de sistemas de qualidade, o RUP se tornou um
padrão para a industria de desenvolvimento de software no mundo. Ele é considerado
como “um framework de processo que pode ser adaptado e estendido para satisfazer as
necessidades de uma organização” (KRUCHTEN, 2000).
“O objetivo do RUP é produzir software de qualidade, que promova uma
solução adequada as necessidades do usuário final.” (KRUCHTEN, 2000) Por isso, é
feita a adoção de casos de uso como elemento fundamental na construção do software,
servindo como base para todo o processo de desenvolvimento.
13
2.1 Estrutura
“Um processo descreve quem está fazendo o que, como e quando. O RUP é
representado usando-se quatro elementos primários de modelagem” (KRUCHTEN,
2000)
• Papéis: quem;
• Atividades: como;
• Artefatos: o que;
• Workflows: quando;
Papéis: Um papel define o comportamento e as responsabilidades de um
indivíduo ou um grupo de trabalho. Os comportamentos são expressos na forma de
atividades. Cada papel está associado a um conjunto de atividades que serão executadas.
“A responsabilidade de um papel está usualmente expressa em relação a certos artefatos
que os papéis criam, modificam ou controlam” (KRUCHTEN, 2000).
Um participante do projeto pode apresentar vários papéis durante o
desenvolvimento do software, e vários participantes podem apresentar o mesmo papel.
Kruchten faz uma analogia de papéis com “um chapéu que o indivíduo usa durante o
projeto. Uma pessoa pode usar vários chapéus” (KRUCHTEN, 2000) e um chapéu pode
ser usado por várias pessoas.
Exemplos de papéis:
• Analista: coordena a análise do domínio, compreensão dos requisitos e a
descrição dos casos de uso.
• Projetista: define as responsabilidades, operações, atributos e associações
de uma ou mais classes e determina sua ambientação no sistema.
Atividades: como visto anteriormente, as atividades descreverão o
comportamento dos papéis. Uma atividade é uma operação que um indivíduo num papel
deve executar, “e que produz um resultado significativo no contexto do projeto”
14
(KRUCHTEN, 2000) Normalmente, a atividade tem como objetivo a criação ou
modificação de um artefato.
A granularidade de uma atividade pode ir de algumas horas até dias. A atividade
deve ser usada como elemento de avanço do projeto. Ela pode se repetir muitas vezes
em cima do mesmo artefato “especialmente de uma iteração para outra, em que o
sistema está sendo refinado e expandido” (KRUCHTEN, 2000). A repetição de uma
atividade deve ser feita pelo mesmo papel.
Exemplo de atividades:
• Procurar Casos de Uso e Atores: executada pelo papel Analista.
• Executar um teste de performance: executada pelo papel Testador de
Performance.
Artefatos: “Um artefato é um pedaço de informação que é produzido, modificado
ou usado por um processo” (KRUCHTEN, 2000) Os artefatos vão sendo construídos e
utilizados ao longo do projeto. Normalmente, servem de entrada para uma atividade que
produz um outro artefato ou modifica este mesmo artefato.
Entre as formas que um artefato pode ter estão: um modelo conceitual do domínio;
um documento com a descrição do domínio do problema, um código fonte.
Um artefato também pode ser a composição de vários artefatos. Por exemplo, o
diagrama de classes de projeto é composto por classes.
A melhor maneira de criar e apresentar artefatos é utilizar-se das ferramentas
apropriadas para a criação dos mesmos. Nem todo artefato é um documento em papel
com texto ou representações. Geralmente eles são o resultado da ferramenta no qual
foram elaborados.
Exemplo de artefatos:
• Um modelo conceitual do Together.
• Um modelo de projeto do Rational Rose.
15
Os artefatos do RUP são classificados em cinco conjuntos de informação:
• Gerencial: artefatos relacionados ao gerenciamento do projeto.
• Requisitos: artefatos relacionados à definição do software a ser
desenvolvido como modelo de casos de uso e descrição de domínio.
• Projeto: artefatos relacionados à descrição do sistema como diagrama de
classes.
• Implementação: código fonte e executáveis.
• Implantação: material de instalação e documentação para o usuário.
Workflow: “é preciso uma forma para descrever uma seqüência de atividades que
produz resultados efetivos e mostre iteração entre os papéis. Um workflow é uma
seqüência de atividades que produz um resultado de considerável valor”. (KRUCHTEN,
2000)
O RUP é composto de nove workflows: seis workflows de engenharia e três
workflows de suporte. Eles representam uma divisão das atividades e papéis em áreas
lógicas. Os seis workflows de engenharia são:
• Modelagem de Negócios
• Requisitos
• Análise e Projeto
• Implementação
• Teste
• Implantação
Os três workflows de suporte:
• Gerenciamento de Projeto
• Gerenciamento de Configuração e Alterações
• Ambiente
16
A seguir serão abordados os workflows utilizados no desenvolvimento da
aplicação de controle de atacado até o presente momento.
2.2 Workflow Modelagem de Negócios
No workflow de modelagem de negócio a equipe irá fazer um estudo do
funcionamento da organização para a qual será desenvolvida uma aplicação. Como
resultado o workflow deve prover informações sobre toda a dinâmica do trabalho para o
qual será construída uma solução de software, identificar todos os processos, seus
problemas e indicar possíveis aperfeiçoamentos.
Principais artefatos desenvolvidos são: Descrição do Domínio, Descrição dos
Casos de Uso do Domínio.
2.3 Workflow de Requisitos
Os principais objetivos do workflow de requisitos são:
• Estabelecer, em acordo com cliente, o que o sistema que será desenvolvido
irá fazer
• Prover mais informações sobre os requisitos do sistema para os
desenvolvedores.
• Definir os limites do sistema (definir que parte do processo de trabalho irá
ser automatizada pela aplicação)
• Estabelecer prazos e custos.
• Definir a interface com o usuário do sistema.
Um requisito é “uma condição ou capacidade que um sistema deve obedecer”
(KRUCHTEN, 2000). Pode-se dividir os requisitos em funcionais e não-funcionais.
Os requisitos funcionais são ações que o sistema deve ser capaz de realizar.
Normalmente, esta ação é fruto de uma ordem externa ao sistema que deve gerar algum
17
resultado. Exemplo de requisitos funcionais: o sistema de supermercado deve registrar a
venda do produto e dar baixa do mesmo no estoque.
Os requisitos não-funcionais não são ações, mas sim características de
funcionamento do sistema. Exemplos de requisitos não-funcionais: o banco de dados
deve suportar mais de 1 milhão de registros; o sistema deve ter capacidade para
trabalhar com leitor de código de barras.
O primeiro passo para coletar os requisitos é elaborar um documento onde se
descreve o que o cliente espera do sistema,. que funções ele espera que o sistema realize
e que resultados devem ser apresentados. Este documento, chamado de documento de
visão, também deve conter as funções de alto nível do sistema. Serão as funções que
farão com que os requisitos do cliente sejam atendidos.
A partir deste documento será elaborado um modelo de caso de uso. “Um caso de
uso é um documento narrativo que descreve a seqüência de eventos de um ator (um
agente externo) que usa um sistema para completar um processo” (JACOBSON, 1992
apud LARMAN, 1997) O modelo de caso de uso é usado como coleta dos requisitos.
No workflow de requisitos também é feita a coleta de informações para projetar a
interface com o usuário do sistema. O modelo de caso de uso servirá como base de
informação para se desenvolver um protótipo de interface com o usuário. Com o
protótipo pronto o cliente e a própria equipe de desenvolvimento podem ter uma noção
da aplicação e assim elaborar os requisitos finais do sistema.
Principais artefatos desenvolvidos neste workflow são: documento de visão,
modelo de caso de uso e protótipo de interface com o usuário.
2.4 Workflow de Análise e Projeto
O objetivo deste workflow é transformar os requisitos em especificações que
descreverão como será feita a implementação do sistema. Para isso deve-se fazer um
estudo dos requisitos e transformá-los num modelo de sistema. Este modelo será então
ajustado de acordo com o ambiente de programação escolhido.
A primeira atividade do workflow consiste na construção de um modelo de análise
a partir do modelo de caso de uso. Através do modelo de casos de uso identificam-se
18
classes significativas para a construção de um modelo conceitual e neste modelo define-
se como estas classes interagem entre si.
Na segunda etapa, usa-se o modelo conceitual de análise para elaborar o projeto
do software. Os elementos do projeto serão baseados nos elementos do modelo
conceitual da análise e adicionam-se os artefatos de software. Descreve-se a
organização do sistema e sua arquitetura de execução.
Na terceira etapa, faz-se o projeto de artefatos de software como banco de dados,
protocolo de comunicação, ou outro artefato necessário ao atendimento dos requisitos
do sistema.
Principais artefatos desenvolvidos neste workflow são: modelo conceitual da
análise, diagrama de classes do projeto.
Os papéis do workflow descritos por Kruchten (2000) são:
Arquiteto: o arquiteto é o coordenador geral do workflow. Ele define a estrutura
geral dos modelos gerados e a interface entre os subsistemas modelados.
Projetista: elabora o modelo de projeto, constituido de operações, atributos,
responsabilidades e relacionamento de uma ou mais classes que definirão a
implementação do sistema. Normalmente um projetista fica responsável pela
modelagem de um ou mais subsistemas definidos pelo arquiteto.
Projetista de Banco de Dados: elabora o modelo de banco de dados, caso seja
necessário.
3 PADRÕES DE ANÁLISE
“Padrões de Análise são grupos de conceitos que representam uma construção
comum em modelagem de negócios. Podem ser relevantes em um domínio, ou abranger
muitos domínios” (FOWLER 1997, p. 8).
Os padrões de análise podem ser definidos como um conjunto de classes e
associações que representam de forma sistemática um contexto de uma aplicação. Esta
representação de contexto muitas vezes é válida para mais de uma aplicação, e isso faz
dos padrões de análise uma valiosa ferramenta de reuso. Fez-se um estudo de vários padrões de análise descritos por Fowler (1997) no
livro Analysis Patterns. Para descrever os modelos, Fowler usou a notação de Odell
(ODELL, 1995 apud FOWLER, 1997). Na transcrição dos modelos de Fowler para este
trabalho foi usada a notação UML.
3.1 Responsabilidade
Nesta seção estão descritos padrões que lidam com o conceito de
responsabilidade. Responsabilidade é um conceito abstrato que pode representar vários
tópicos como hierarquia de organização, vínculo empregatício, etc.
3.1.1 Entidade
Este é, freqüentemente, um dos primeiros padrões com que um analista iniciante
se depara. O padrão Entidade é utilizado para definir pessoas e organizações dentro do
modelo.
Como organizações e pessoas tem vários atributos comuns, como nome, endereço
e formas de contato (telefone, e-mail), pode-se generalizar pessoa e organização numa
classe chamada Entidade (figura 3.1).
20
Figura 3.1 – Padrão Entidade
Mas não são só os atributos que tornam Pessoa e Organização especificações da
mesma classe. Várias ações como pagamentos, envio de correspondência (e-mail ou
carta), pagamento de impostos e muitas outras são comuns às duas classes.
Fowler (1997) faz uma analogia de Entidade com uma agenda telefônica pessoal.
A maioria dos registros de uma agenda são de pessoas, mas alguns podem ser de
empresas, e nenhuma diferenciação é vista entre os registros de pessoas e empresas.
Logo abaixo do telefone de Paulo, por exemplo, pode estar o telefone da Panificadora
Pão Quente.
Segundo Fowler (1997), este padrão representa um caso típico de conceito não
nomeado:
“conceito não nomeado – todo mundo conhece e usa, mas ninguém tem
um nome para ele. Eu já o vi em incontáveis modelos de dados com vários
nomes: person/organization, player, legal entity e muitos outros”.
(FOWLER, 1997)
21
No Brasil encontra-se este padrão com nome de Pessoa/Organização ou mesmo
Pessoa, que generaliza Pessoa Física e Pessoa Jurídica.
3.1.2 Hierarquia de Organizações
Considere uma multinacional qualquer: Cervejaria Kicerva Ltda por exemplo. Ela
tem várias unidades operacionais espalhadas pelo mundo, as quais são divididas em
regiões, que por sua vez são divididas em sub-regiões, e estas divididas em escritórios
de venda. Pode-se representar esta situação com o seguinte modelo (figura 3.2):
Figura 3.2 – Modelo de hierarquia simples
Apesar de ser satisfatória a representação acima é pouco flexível. Se, por exemplo,
a Kicerva deseje fechar os escritórios sub-regionais (as regiões gerenciariam
diretamente os escritórios de venda), seriam necessárias alterações no modelo.
O modelo de hierarquia com associação recursiva (figura 3.3) possibilita essa
flexibilidade. Para definir as hierarquias no modelo, adiciona-se restrições aos subtipos
de organização para que eles saibam quem deve ser sua subordinada.
22
Figura 3.3 – Modelo de hierarquia com restrições
Caso haja uma mudança na hierarquia das organizações da empresa, simplesmente
altera-se a restrição de um subtipo. Neste ponto ganha-se flexibilidade, já que
geralmente é mais fácil mudar uma restrição do que alterar a estrutura de um modelo.
3.1.3 Estrutura de Organizações
Apesar de ser bastante flexível, algumas situações não podem ser representadas no
modelo de hierarquia com restrições (figura 3.3). Suponha que os escritórios de venda
da Kicerva sejam subordinados a uma sub-região, a qual devem reportar sobre suas
vendas, e subordinado também a uma unidade operacional de produção, a qual devem
reportar sobre a qualidade do produto Kicerva (o escritório é subordinado a duas
organizações).
Para representar este caso transforma-se a associação de hierarquia (associação
gerencia) em um conceito, e diferenciam-se os vários tipos de hierarquias através de
outro conceito, o conceito Tipo de Estrutura, como mostra a Figura 3.4.
23
Figura 3.4 – Estrutura de Organização
Exemplo 1: O escritório de vendas da Kicerva em Florianópolis está subordinado
à sub-região de vendas do Sul e a unidade operacional de qualidade de produto. Esta
representação seria feita com dois Tipos de Estrutura: vendas e qualidade de produto. O
Tipo de Estrutura venda definiria uma Estrutura de Organização com Sub-Região como
gerente e Escritório de Venda como subordinada. O Tipo de Estrutura qualidade de
produto definiria uma Estrutura de Organização com Unidade Operacional como
gerente e Escritório de Venda como subordinada.
Para apenas duas hierarquias, como no exemplo acima, talvez o modelo seja
subutilizado. Mas em casos onde há vários tipos de hierarquia, este modelo fornece a
flexibilidade necessária para lidar com tal complexidade.
As restrições, que antes eram definidas nos subtipos de Organização, agora podem
ser definidas no Tipo de Estrutura. Isso facilita a adição de novos Tipos de Estrutura,
mas por outro lado, dificulta a adição de novos subtipos de Organizações, já que cada
adição de um novo subtipo provoca uma alteração nas restrições de todos os Tipos de
Estruras que definem a hierarquia do subtipo adicionado.
24
Então a escolha de onde definir as restrições depende de uma análise do modelo
para verificar que área deste sofrerá mais adição de novos elementos. “Projete um
modelo para que as modificações mais frequentes do modelo cause o menor número de
alterações dos conceitos” (FOWLER 1997).
3.1.4 Responsabilidade
Pode-se estender o padrão anterior para ser usado não apenas com organizações,
mas com qualquer tipo de entidade que possua algum tipo de relacionamento de
responsabilidade.
O padrão Responsabilidade usa o mesmo modelo de Estrutura de Organizações,
apenas dando novos nomes aos conceitos e usando o padrão Entidade no lugar das
organizações. (figura 3.5)
Figura 3.5 – Hierarquia utilizando padrão Entidade
25
Exemplo 1: João da Silva trabalha na Cervejaria Kicerva. Isto pode ser modelado
como uma Responsabilidade na qual o responsável é a Kicerva e o dependente é João
Boanerges. O Tipo de Responsabilidade é emprego.
Exemplo 2: João da Silva é o gerente da equipe de vendas de Florianópolis. Isto
pode ser modelado como uma Responsabilidade em que o Responsável e João, o
dependente é a equipe de vendas de Florianópolis e o Tipo de Responsabilidade é
gerência.
3.1.5 Nível de Informação de Responsabilidade
O número de tipos de responsabilidade que se consegue representar é muito maior
que o número de tipos de estrutura no modelo de Estrutura de Organizações. Por isso as
restrições e regras entre as relações de dependência ficam bem mais complexas no
modelo Responsabilidade.
Essa complexidade pode ser gerenciada dividindo-o em duas camadas: uma de
nível operacional e uma de nível de informação. O nível operacional abriga conceitos
como Responsabilidade, Entidade e seus relacionamentos. O nível de informação abriga
conceitos como Tipo de Responsabilidade, Tipo de Entidade (figura 3.6).
O nível operacional registra atividades do dia-a-dia do domínio. O nível de
informação regulamenta essas atividades. No modelo da figura 3.6, uma instância de
Responsabilidade sofre as restrições definidas entre as relações de Tipo de
Responsabilidade e Tipo de Entidade.
26
Figura 3.6 – Divisão em camadas
Neste modelo, a associação “especifica” substitui as especializações de entidade.
Assim, Entidade pode ter seu comportamento definido pelo Tipo de Entidade ao qual
ele está associado.
A reflexão entre o nível de informação e o nível operacional é bastante parecida,
mas não igual. No nível operacional as associações dependente e responsável são de 1
para muitos, já no nível de informação elas passam a ser de muitos para muitos, pois o
nível operacional registra somente a Entidade em uso na operação, enquanto o nível de
informação registra todas as associações possíveis entre Entidades e Responsabilidades.
A divisão dos modelos em níveis de informação e operacional é comum, mas nem
sempre são explicitadas. Porém, isso ajuda a identificar melhor os comportamentos dos
conceitos no modelo.
27
3.2 Estoque e Contabilidade
Uma grande quantidade de sistemas computacionais comerciais é desenvolvida
para gerenciar a movimentação de dinheiro de uma empresa, basicamente registrando
como o dinheiro é ganho e depois gasto. A idéia básica por trás do gerenciamento de
contabilidade e estoque é que existem vários recipientes contendo dinheiro ou produtos
e é preciso registrar a movimentação do dinheiro e dos produtos através desses
recipientes.
Desta idéia básica nasceram os padrões Estoque e Contabilidade que serão
descritos neste trabalho. Eles apresentam uma coleção de conceitos que pode-se usar
como base para sistemas de contabilidade, controle de estoque e gerenciamento de
recursos.
3.2.1 Padrão Conta
O padrão Conta fornece conceitos básicos para o gerenciamento de
movimentação de um produto qualquer em um estoque genérico, como por exemplo,
litros de combustível em um posto de gasolina ou quantidade de dinheiro em um caixa
eletrônico.
Para isso é preciso detalhar as operações que modificam a quantidade dos
mesmos neste estoque.
No padrão Conta isso é feito registrando-se um Lançamento em uma respectiva
Conta a cada modificação no estoque destes produtos (Figura 3.7).
Figura 3.7 – Conta e Lançamento
28
Em Lançamento tem-se a data_lançamento que é a data real em que a operação
foi feita e a data_registro que é a data em que a operação foi registrada no sistema.
Em Conta tem-se o atributo saldo que é a soma da quantidade de todos os seus
Lançamentos.
Um Lançamento será definido como depósito ou retirada de acordo com o sinal
de seu valor quantidade.
Através dos Lançamentos um usuário do sistema pode visualizar a
movimentação da Conta em determinado período de tempo.
Exemplo 1: O Restaurante Universitário comprou 100 Kg de feijão esta semana.
Isso é representado como um Lançamento de 100 na Conta de estoque de feijão do RU.
Exemplo 2: André pagou a prestação de R$ 250,00 de seu carro. Isso é
representado como um Lançamento de –250 na Conta de finanças pessoais de André.
3.2.2 Transações
No padrão Conta tem-se o registro da movimentação de um certo produto em um
estoque ou uma conta. Mas em alguns casos é necessário registrar a movimentação de
um produto de um local para outro. Por exemplo, suponha que uma pessoa tem várias
contas correntes, em bancos diferentes. Neste caso, o padrão Conta não pode registrar a
movimentação de dinheiro entre essas contas.
Uma Transação assume que toda retirada em uma Conta gerará um depósito no
mesmo valor, em outra Conta, e vice-versa. Assim registra-se a origem e o destino de
todas as movimentações (Figura 3.8).
Para explicar o conceito de Transação, Fowler (1997) usa o princípio da
conservação, que diz que o dinheiro (ou qualquer outro produto) nunca é criado ou
destruído e sim movimentado de um local (conta) para outro. Uma Transação contém
uma restrição que diz que a soma de seus lançamentos precisa ser igual a 0 (zero).
Assim o dinheiro é sempre movimentado, nunca criado ou destruído.
29
Figura 3.8 - Transação
Exemplo: Pedro transferiu R$ 1000,00 de sua conta no Banco Brasileiro para sua
conta no Banco Catarinense. Isso é representado por uma Transação com um
Lançamento de -1000 na Conta Banco Brasileiro e outro Lançamento de 1000 na Conta
Banco Catarinense.
Em estruturas mais complexas este padrão é uma boa alternativa ao padrão Conta,
pois além de ter mais informações sobre as movimentações, o princípio de conservação
usado neste padrão ajuda a previnir falhas no sistema.
3.2.3 Transações Múltiplas
O padrão Transação limita-se a representar uma única retirada de alguma Conta e
um único depósito em outra Conta. Assim não há possibilidade de representar a
movimentação de várias Contas em uma única transação.
Por exemplo, se Pedro retirou R$ 1000,00 de sua conta no Banco Brasileiro, e
retirou R$ 1200,00 do Fundo de Ações e depositou o total desse dinheiro na sua conta
do Banco Catarinense, esta movimentação tem de ser representada por duas Transações,
apesar de Pedro ter feito apenas um único depósito na Conta do Banco Catarinense.
Para representar a movimentação de Pedro em uma única Transação usa-se o
modelo de Transações Múltiplas (Figura 3.9).
30
Figura 3.9 – Transação Múltipla
Assim há a possibilidade de se associar dois ou mais Lançamentos a uma
Transação. O princípio da conservação continua valendo neste caso. Assim a Transação
simples (com apenas dois Lançamentos) se torna um caso particular da Transação
Múltipla.
3.2.4 Conta Resumida
Para sistemas de contabilidade um pouco mais complexos, utiliza-se o padrão
Conta Resumida, que faz um agrupamento de várias Contas numa única Conta
Resumida. Este tipo de estruturação pode ser feito conforme o modelo da Figura 3.10
Figura 3.10 – Conta Resumida
31
Exemplo: Na contabilidade de Simão há uma Conta Resumida para gastos
essenciais que agrupa as seguintes Contas Detalhadas: gastos combustível, gastos saúde,
gastos mercado.
Neste modelo apenas, as Contas Detalhadas podem ter Lançamentos. Para calcular
o saldo de uma Conta Resumida, esta consultará as Contas nela agrupadas. Nota-se que
uma Conta Resumida pode agrupar várias Contas Resumidas, criando assim uma árvore
de Contas.
3.2.5 Conta Gatilho
Este padrão teve como base o controle do valor dos impostos sobre movimentação
de dinheiro.
Normalmente, o valor dos impostos de uma empresa só é conhecido quando esta
realiza um balanço no fim do mês, ou do ano. Usando este padrão o sistema irá
contabilizar o valor total do imposto a cada entrada no caixa da empresa.
Ele funciona da seguinte maneira: Para cada Lançamento de entrada de dinheiro
na conta caixa da empresa, haverá também um Lançamento Gatilho em uma outra
conta, uma Conta Gatilho. Este Lançamento Gatilho terá como valor a quantidade de
imposto gerado pelo Lançamento de entrada de dinheiro. Assim, a Conta Gatilho terá no
seu saldo o valor do imposto a ser pago sempre atualizado.
Assim uma empresa pode saber, a qualquer momento, o valor que ela terá que
pagar em impostos de acordo com a movimentação de dinheiro que ela já fez.
Exemplo 1: Quando João recebe o pagamento de um cliente, ele paga 25% deste
pagamento em impostos. João então registra um Lançamento na sua conta de
pagamentos e também um Lançamento na Conta Gatilho Imposto. O Lançamento na
Conta Gatilho será 25% do valor do Lançamento na Conta de João.
32
3.2.6 Regra de Lançamento
No exemplo da Conta Gatilho para imposto, o usuário deve sempre realizar dois
lançamentos no sistema para registrar a taxação da movimentação. Um lançamento para
a conta real e um lançamento registrando o valor da taxação.
Se o imposto tem um valor fixo, como 25% no exemplo anterior, pode-se
programar o sistema para realizar os lançamentos na Conta Gatilho automaticamente,
simplesmente criando uma regra para as contas as quais os lançamentos são taxados.
A regra calcularia o imposto de todos os lançamentos de uma conta associada
como gatilho e geraria um lançamento, no valor calculado, numa Conta Gatilho,
associada à Regra de Lançamento como saída. (figura 3.11).
Figura 3.11 – Regra de Lançamento
Exemplo: Todo pagamento recebido por um serviço é taxado em 12% pelo ISS
(imposto sobre serviços). Modela-se esta taxação com a Conta pagamento de serviços
sendo o gatilho da Regra de Lançamento cálculo de ISS e a saída desta Regra sendo a
Conta total do ISS.
Há casos em que o cálculo do valor do imposto é um pouco mais complexo, e não
apenas uma mera multiplicação. Um exemplo é o cálculo de impostos gradativos, em
que movimentações abaixo de um valor são isentas de cobrança, e acima de outro valor,
tem uma cobrança diferenciada.
Para estes casos é necessário um algoritmo arbitrário para o cálculo do imposto.
Então um Método de Cálculo é associado a classe Regra de Lançamento. Assim cada
instância de Regra de Lançamento terá seu próprio Método de Cálculo (figura 3.12).
33
Figura 3.12 – Metodo de Cálculo para saida
3.2.7 Regras de Lançamentos para várias contas
Pode-se estender o padrão Regra de Lançamento de maneira que uma única regra
possa tratar de várias contas, sem a necessidade de criar uma regra para cada conta do
sistema.
Assim, tomando uma empresa genérica como exemplo, pode-se ter uma Conta
para cada empregado e apenas uma Regra de Lançamento para tratar o cálculo do
imposto de renda sobre o salário dos trabalhadores.
Pode-se modelar a solução de duas maneiras: a primeira é dividir o modelo em
dois níveis: nível de informação e nível operacional. Colocam-se as Regras de
Lançamento no nível de informação e associam-nas com Tipo de Conta. Assim pode-se
ter Tipo de Conta para salário, horas-extras, etc. No nível operacional colocam-se as
contas reais do domínio da aplicação (figura 3.13).
34
Figura 3.13 – Conta e Tipo de Conta
Quando um Lançamento for feito na Conta, esta vai executar as regras de
lançamento do Tipo de Conta associado a ela.
A segunda maneira proposta é usar Contas Resumida (seção 3.2.4). Uma Regra de
Lançamento é definida para uma conta resumo e quando um lançamento é feito em uma
conta detalhada, esta executará a Regra de Lançamento de sua conta resumo.
A escolha entre os dois métodos depende do comportamento do Tipo de Conta.
Caso a divisão de nível de informação e nível operacional seja bem clara, e todas as
regras de lançamento estejam associadas ao Tipo de Conta e os lançamentos só são
feitos em uma conta, então o modelo de níveis é mais apropriado.
Mas em muitos casos os lançamentos podem ser feitos em níveis mais gerais.
Lançamentos podem estar associados a Tipos de Conta e regras de lançamentos
associadas a contas, o que infringiria a divisão entres os níveis operacionais e de
informação. Neste caso o melhor é usar o modelo da segunda proposta, com contas
resumo.
35
Pode-se estender os modelos para abranger situações mais complexas, como casos
em que a conta de saída da regra de lançamento pode mudar de acordo com certos
parâmetros. Como exemplo, tem-se o caso em que o desempenho de um programador
dentro de um projeto afeta o salário do gerente deste projeto. Neste caso, a regra de
lançamento teria a conta desempenho do programador como gatilho, e deve “procurar”
a conta salário do gerente do projeto para colocar como saída. Isso se faz através da
associação da regra de lançamento a um método de procura (figura 3.14).
Figura 3.14 – Localizador de Conta
O método localizador vai consultar a conta gatilho desta regra para saber qual
programador é a fonte deste lançamento. Consultando o programador, o método obtem
informações sobre o projeto deste programador e o seu gerente. Assim o método
localizador encontra a conta salário do gerente, que irá receber um lançamento de bônus
pelo desempenho deste programador.
3.3 Comércio
Neste capítulo serão descritos padrões que realizam o controle de compra e venda
de produtos, e como o valor destes produtos pode modificar as condições do mercado.
Os padrões descrevem como uma instituição pode registrar suas operações comerciais
para usar como base para tomada de decisões.
36
3.3.1 Contrato
O tipo mais simples de relação comercial é de uma entidade (pessoa/organização)
comprando um produto de outra entidade. O produto é qualquer item comercializável,
como por exemplo, sacas de café e toneladas de ferro. Um simples modelo como da
figura 3.15 pode representar essa operação.
Em contrato representa-se uma transação comercial como um Contrato. Um
Contrato pode ser uma compra, uma venda ou qualquer outra transação feita entre
entidades. Ele tem como atributos Quantidade e Preço. O produto a ser comercializado é
representado como um Produto que está associado a Contrato (Figura 3.15).
Figura 3.15 – Contrato com especificação
Exemplo: a UFSC registra a compra de 500 resmas de papel Folhex ao preço de
R$ 8,00 cada, como sendo uma Compra com o Contratante sendo a Folhex Fornecedora
de Papel e o Produto sendo resmas de papel. Preço fica em 8 e quantidade 500.
As operações que serão representadas em Contrato nem sempre precisam ser
especificações do mesmo. Para operações de Compra e Venda, tem-se como alternativa
à especificação a criação de duas associações entre Contrato e Parte (Figura 3.16).
37
Figura 3.16 – Contrato com associações
Este segundo modelo também possibilita que sejam registradas transações com
mais de uma Entidade de ambos os lados (vendedor e comprador).
Exemplo: A UFSC deseja registrar a compra de resmas de papel do Centro
Tecnológico. O sistema da UFSC registraria como um Contrato com Folhex
Fornecedora de Papel como vendedor e o CTC como comprador.
Um modelo mais abrangente utilizando Contratos pode ser definido fazendo a
junção dos dois primeiros modelos. Os dois lados da transação foram definidos como
Contratado e Contratante e as especificações de contrato serão as operações que podem
ser realizadas, com seus devidos comportamentos (Figura 3.17).
Figura 3.17 - Contrato
38
O diferencial destes dois últimos modelos é que os contratos registrados não
precisam necessariamente envolver a Entidade que controla o sistema. Caso seja válido,
pode-se registrar transações comerciais entre Entidades externas ao sistema.
Nota-se a possibilidade de relacionar este modelo com o modelo de Transações do
capítulo anterior. Um contrato de venda, por exemplo, pode ser representado por uma
Transação que faz um Lançamento de um produto na Conta de um comprador e faz
outro Lançamento de dinheiro na Conta de um vendedor.
3.3.2 Portfólio
Raramente considera-se um modelo de contratos sozinho. Tipicamente uma
empresa que gerencia contratos vai analisar um grupo de contratos de um certo tipo para
estimar os riscos em contratos futuros. A seleção dos contratos que serão analisados
pode ser feita pelo tipo de produto comercializado, pela entidade contratante ou por
qualquer outra propriedade relevante de contrato.
Um Portfólio representa este grupo, ou seja, uma coleção de contratos que irão ser
avaliados (figura 3.18).
Figura 3.18 - Portfólio
A seleção dos contratos associados a um Portfólio pode ser feita através de um
método que recebe um Contrato como parâmetro. Caso o método retorne um valor
verdadeiro, o Contrato pertencerá àquele Portfólio (figura 3.19).
39
Figura 3.19 – Seleção de Contratos por método booleano
Outra maneira para a construção da coleção dos contratos que pertencerão a um
portfólio é a implementação de um Seletor de Contratos. Este seletor irá combinar
alguns atributos de Contrato, como contratante, produto, data de início, etc. e os
utilizará para filtrar um grupo particular de contratos. (figura 3.20).
40
Figura 3.20 – Seleção de Contratos por comparação com Seletor
Apesar de ser menos abrangente que um método de seleção, o Seletor de
Contratos é bem mais flexível, podendo ser modificado em tempo de execução apenas
alterando o valor de suas propriedades.
Portfólios podem ser temporários ou persistentes. Porfólios temporários são
criados por demanda. Um filtro é especificado e todas as instâncias de Contrato são
consultadas. Ao fim da utilização, o Portfólio perde a sua coleção de Contratos.
Portfólios persistentes são criados da mesma maneira, mas não são descartados ao
fim de sua utilização. Quando um novo Contrato é criado, este passa pelos filtros dos
Portfólios permanentes. Caso o novo Contrato preencha os requisitos do filtro, é
anexado ao Portfólio.
“Portfólios são úteis em muitos domínios. A característica essencial de um
Portfólio é a de que é um objeto que encapsula um mecanismo de seleção de um grupo
de objetos” (FOWLER, 2000)
4 PADRÕES DE PROJETO
Pode-se definir padrões de projetos como um conjunto de classes de software
que descrevem uma solução comum para determinados tipos de problemas. Essas
classes têm papéis bem definidos e interagem entre si para construir uma solução lógica
que será implementada em uma linguagem de programação.
No decorrer do desenvolvimento do trabalho foi feito um estudo de alguns dos
padrões de projeto do livro Design Patterns de Erich Gamma (1994). Segundo o próprio
Gamma, os padrões descritos em seu livro “são descrições de classes e objetos
comunicantes, que são configuradas para resolver algum problema comum de projeto de
software, dentro de um contexto particular”.
Gamma (1994) classifica os padrões em 3 grupos distintos, levando em conta o
tipo de problema ao qual o padrão se destina: criação, estrutural e comportamental
(creational, structutral and behavioral).
Padrões de criação delegam partes do processo de criação de um objeto para
subclasses ou para outros objetos. Padrões estruturais usam herança para compor as
classes. Padrões comportamentais usam herança para implementar algorítmos e fluxos
de controle.
Os padrões estudados foram o Singleton (criação), Factory Method (criação),
Proxy (estrutural), Facade (estrutural), State (comportamental), Strategy
(comportamental) e Composite (estrutural). Mais a frente será discutida a viabilidade do
uso dos padrões no projeto da aplicação de Controle de Atacado.
4.1 Singleton
O padrão Singleton é usado para assegurar que uma referida classe possa ter
apenas uma instância no sistema e essa instância tenha um ponto de acesso global. Pode
ser usado, por exemplo, num sistema de impressão que contenha apenas um spooler,
42
num sistema operacional, que deve contar apenas um sistema de arquivos ou num
sistema de controle de estoque, que deve ter apenas um caixa.
Neste padrão, a classe de instância única, ou classe Singleton, será responsável
em assegurar que apenas uma instância sua poderá ser criada. Para isso, o construtor
dessa classe deve ser uma operação de classe (static em Java ou C++). O acesso à
instância também se dará através de uma operação de classe.
A classe Singleton faz o encapsulamento da sua instância única, armazenada
num atributo da própria classe. Isso permite total controle de quando e como se dará o
acesso à instância (Figura 4.1).
Figura 4.1 – Estrutura do Padrão Singleton
Para implementar uma classe Singleton em Java, declara-se o construtor como
privativo. O acesso a classe se dará através do método obterInstancia que fornecerá
sempre a mesma instância. Caso nenhuma instância da classe tenha sido criada, o
método cria a instância.
public class Singleton { private static Singleton instancia; private Singleton() {} public static Singleton obterInstancia() { if (instancia == null) { instancia = new Singleton(); } return instancia; }
43
Pode-se usar o padrão Singleton para substituir classes totalmente estáticas
(classes sem instâncias, apenas com métodos estáticos) com a vantagem de o padrão
possibilitar o uso de subclasses da classe estática (classes totalmente estáticas não
podem ter seus métodos sobrescritos, não suportam polimorfismo).
4.2 Factory Method O padrão Factory Method é geralmente utilizado em situações onde a definição do
tipo de objeto a ser criado só é especificada na subclasse de uma classe. A utilização do
Factory Method dá mais flexibilidade a classe por deixar que sua subclasse tome a
decisão de que tipo de objeto será craido.
Pode-se citar o exemplo da classe Desenho para demonstrar a utilização do
Factory Method. A classe abstrata desenho trabalha com os objetos da classe abstrata
FormaGeometrica, que generaliza as classes Circulo, Triangulo e Quadrado. Um
método para da classe Desenho sem o uso do Factory Method ficaria da seguinte forma:
public class desenho { public void desenharCasa() { telhado = new Triangulo(); parede = new Circulo(); telhado.desenhar; parede.desenhar; } }
Esse tipo de implementação torna a classe Desenho pouco extensível. Caso haja
necessidade de criar uma extensão de desenho, como por exemplo DesenhoColorido,
utilizando nessa extensão as classes CirculoColorido, TrianguloColorido, e
QuadradoColorido, o método desenharCasa teria que ser refeito.
O padrão Factory Method torna as extensões da classe Desenho mais simples
utilizando-se de "métodos de criação", ou factory methods, para instanciar as classes
Circulo, Triangulo e Quadrado. Abaixo o método de criação criarForma:
44
public class Desenho { public FormaGeometrica criarForma(forma) { if (forma == "circulo") { r = new Circulo(); } else { if (forma == "triangulo") { r = new Triangulo(); . . . return r; } }
O método desenharCasa fica da seguinte forma
public void desenharCasa() { telhado = criarForma("triangulo"); //não há definição da classe parede = criarForma("quadrado"); //não há definição da classe telhado.desenhar; parede.desenhar; }
Dessa maneira, para estender a classe Desenho para DesenhoColorido, reescreve-
se apenas os métodos de criação.
public class DesenhoColorido extends Desenho { public FormaGeometrica criarForma(forma) { if (forma == "circulo") { r = new CirculoColorido(); //modificacao da extensao da
classe } else { if (forma == "triangulo") { r = new TrianguloColorido(); //modificacao da
extensao da classe .
45
4.2.1 Estrutura
A estrutra do padrão é representada por quatro classes: Produto,
ProdutoConcreto, Criador e CriadorConcreto (figura 4.2).
O Produto define a interface do objeto a ser criado. No exemplo anterior, o
Produto seriam as classes Triangulo, Circulo e Quadrado.
ProdutoConcreto é a extensão da classe Produto. As classes TrianguloColorido é
um ProdutoConcreto de Triangulo.
No Criador é declarado o método criador, ou factory method, que retorna um
objeto da classe Produto. A classe Desenho é um criador e criarForma é o factory
method.
O CriadorConcreto reescreve o método criador para retornar uma instância de
ProdutoConcreto (figura 4.2).
Figura 4.2 – Estrutura do Factory Method
46
4.2.2 Implementação
Como visto no exemplo, a implementação de um Factory Method é
relativamente simples. Na classe Criador o factoryMethod pode ser abstrato, contendo
apenas a assinatura do método. Na classe CriadorConcreto o factoryMethod é
definitivamente implementado, podendo ter como argumento, variáveis que tenham
influência na decisão da classe concreta a ser criada.
Classe Criador:
public abstract class Criador { public Produto factoryMethod(); }
Classe CraidorConcreto:
public class CriadorConcreto extends Criador { public Produto factoryMethod() { return new ProdutoConcreto(); } }
4.3 Proxy O padrão Proxy tem como objetivo “prover um substituto ou ponto de acesso”
(GAMMA, 1994) que fará o controle de acesso a outro objeto. Dessa maneira, pode-se
prorrogar os custos da criação de um objeto até que o mesmo seja realmente utilizado,
ou fazer a representação de um objeto remoto localmente.
Numa aplicação com acesso à banco de dados, uma consulta ao banco pode ser
uma tarefa de alto custo. Um objeto persistente pode ter uma propriedade complexa, que
necessita de várias junções de tabelas para ser obtida. O Proxy evita que a definição de
uma propriedade complexa seja feita na construção do objeto, e prorroga a definição
47
dessa propriedade até que seja realmente necessária. Isto torna todo o trabalho mais
eficiente.
Para isso cria-se um intermediário (proxy) do objeto persistente. O objeto
intermediário terá uma interface em comum com o objeto persistente, além de ter uma
referência para o mesmo (figura 4.3).
Figura 4.3 – Exemplo do Proxy
4.3.1 Implementação
Como foi dito anteriormente, a classe Intermediário terá uma referência para o
objeto real. No caso específico do exemplo, a diferença das classes Intermediário e
PersistenteReal estará no construtor das mesmas. O construtor da classe Intermediário
não fará a consulta para obter propriedadeComplexa.
public class Intermediario implements Persistente { private PersistenteReal real; protected int propriedadeSimples; protected String propriedadeComplexa;
48
protected BD BancoDeDados public Intermediario() { BD = BancoDeDados.obterBanco(); propriedadeSimples = BD.consulta(consultaSimples); } public String obterPropriedadeComplexa() { if (real == null) { real = new PersistenteReal(); } return real.obterPropriedadeComplexa(); }
}
Nota-se que uma instância de Intermediario só inicializa propriedadeSimples.
Quando propriedadeComplexa é requistada, Intermediario faz a criação de uma
instância de PersistenciaReal, e desta instância obtém a propriedadeComplexa.
Caso haja necessidade de consultar a propriedadeSimples de uma grande
quantidade de dados, o uso do Proxy fará com que a tarefa fique muito mais eficiênte.
4.3.2 Estrutura
SujeitoReal é a classe original, que terá um substituto equivalente com uma
interface em comum.
O Proxy é a classe que irá substituir SujeitoReal. Proxy referencia SujeitoReal
e as duas compartilham a mesma interface (figura 4.4).
49
Figura 4.4 – Etrutura do Proxy
Algumas variações do Padrão Proxy são:
Remote proxy - fornece uma representação local para um objeto remoto;
Protection proxy – faz controle de acesso ao objeto real. Útil para quando o
objeto real possui acesso restrito;
Virtual proxy – prorroga criação de objetos com alto custo de criação (exemplo
de classe persistente descrito anteriormente);
4.4 Facade
O objetivo do padrão Facade é unificar em uma interface a manipulação de
vários objetos de um subsistema. Assim, as classes do núcleo de cada subsistema podem
ser modificadas sem que a interface para o cliente seja afetada. Em um conjunto de
subsistemas, isso diminui a interdependência entre os mesmos. Outra consequência de
seu uso é a diminuição da complexidade de utilização dos subsistemas, pois reduz o
número de objetos que precisam ser acessados externamente.
50
Pode-se exemplificar o uso do Facade supondo que o sistema de matrícula e
alocação de salas da UFSC tenha a estrutura mostra na Figura 4.5:
Figura 4.5 – Sem o uso do Facade
O cliente do sistema precisa conhecer várias classes, promovendo um
acomplamento forte entre o sistema e o cliente. Utilizando o padrão Facade, o sistema
fornece uma interface única para o cliente, facilitando a utlização e diminuindo o
acoplamento. No exemplo da Figura 4.6 matricula_aloca é um Facade para o sistema de
alocação e matrícula.
51
Figura 4.6 – Com o uso do Facade
4.4.1 Implementação
A classe Facade deve implementar operações gerais do sistema. O acesso ao
sistema deve ser feito unicamente pela classe Facade.
Implementação sem Facade:
public class Cliente { public void matricularCalouro(Aluno umAluno, Curso curso) { Turma[] turmas1fase = curso.materias1fase(); for (int i = 0; i < turmas1fase.length; I++) { turmas1fase[I].adiciona(umAluno); } curso.adicione(umAluno); } }
52
Usando Facade:
public class Matricula_aloca { public void matricular(Aluno oAluno, Curso oCurso, int fase) { . . . }
Simplificação da classe Cliente:
public class Cliente { Matricula_aloca sistemaMatriculaAloca public void matricularCalouro(Aluno umAluno, Curso curso) { sistemaMatriculaAloca.matricular(umAluno, curso, 1);
} }
4.4.2 Estrutura
O Facade cria uma interface única para todo sistema como mostra a Figura 4.7
Figura 4.7 – Estrutura do Facade
53
4.5 State O Padrão State implementa o conceito de “estado de classe”. Ele permite que
uma classe possua vários estados e que a alteração destes estados possa ser feita em
tempo de execução, mudando também o comportamento da classe.
Exemplo 1: Em uma empresa, várias operações, como calculo de impostos,
dependem da classificação da mesma em micro, pequena ou média empresa. Esta
classificação é feita baseando-se no número de empregados da empresa. Neste caso
pode-se utilizar o padrão State para modelar o sistema como mostra a Figura 4.8:
Figura 4.8 – Padrão State
Cada subclasse de Tipo terá sua própria implementação do calculo do imposto.
Assim empresa repassa esta responsabilidade para uma dessas subclasses. O estado da
empresa pode ser definido toda vez que a propriedade n_empregados for alterada.
54
4.5.1 Estrutura
A classe Contexto representa um objeto real que possui vários estados. Contexto
terá uma instância de um EstadoConcreto que definirá seu estado atual.
A classe Estado define uma interface comum para as implementações dos
estados de Contexto.
Cada classe EstadoConcreto implementa um estado real do Contexto.
Pode-se ver o diagrama da estrutura na Figura 4.9:
Figura 4.9 – Estrutura do State
4.5.2 Implementação
Seguindo o padrão, a classe Empresa (exemplo 1) fica da seguinte maneira:
public class Empresa { public final Tipo Micro = new MicroEmpresa(); public final Tipo Pequena = new PequenaEmpresa(); public final Tipo Media = new MediaEmpresa(); protected n_empregados; private Tipo tipo_empresa;
55
public float calcularImposto() { return tipo_empresa.calcularImposto(); } public void alterar_n_empregados(int n_emp) { if (n_emp > 20) { altere_tipo_empresa(Pequena); } else { if (n_emp > 100) { altere_tipo_empresa(Media); } else { altere_tipo_empresa(Micro); } }
n_empregados = n_emp; } private void altere_tipo_empresa(Tipo tipo_emp) { tipo_empresa = tipo_emp; } }
Nota-se que a classe possui associações com as instâncias que implementam
seus estados.
Quando requisistada uma alteração no atributo n_empregados, o procedimento
altere_n_empregados define o tipo de empresa verificando o argumento n_emp e
chamando a função altere_tipo_empresa.
O padrão State não especifica que classe de sua estrutura deve definir o estado
atual de Contexto. Na implementação a definição foi feita na classe Empresa, pois esta
56
possui o conhecimento do critério de alteração dos estados. Em alguns casos define-se a
alteração do estado nas classes de EstadoConcreto.
4.6 Composite
O padrão Composite é usado em aplicações em que se utiliza composição de
objetos individuais para implementação de objetos mais complexos. Este padrão
permite que a forma de manipulação dos objetos seja a mesma, tanto para objetos
simples como para objetos complexos (compostos).
4.6.1 Estrutura
Figura 4.10 – Estrutura do Composite
A classe abstrata Componente declara uma interface para os objetos da
composição e implementa os comportamentos que são comuns aos objetos simples e
complexos.
57
A classe ComponenteComplexo representa um objeto composto. A associação
filho permite que esta composição seja tanto de objetos simples como de objetos
complexos.
ComponenteSimples representa o objeto simples.
4.6.2 Implementação
A classe abstrata Componente implementa apenas os métodos comuns aos dois
tipos de objetos, definindo o restante dos métodos como abstratos.
A classe ComponenteComplexo implementa os métodos de manipulação dos
“componentes filhos” como inserir, remover e retornarFilho.
public class ComponenteComplexo extends Componente {
private Vector composicao;
public void inserir(Componente filho) {
composicao.add(filho);
}
public Componente retornarFilho(int id) {
return (Componente)composicao.get(id);
}
}
A classe ComponenteSimples não possui associação com nenhum outro
componente e não implementa os métodos de manipulação de “filhos”.
58
4.7 Strategy
O padrão Strategy cria a possibilidade de se definir mais de um tipo de algorítmo
para um mesmo método. É útil nos casos onde a implementação do método depende de
algum fator externo à classe em que será implementado.
4.7.1 Estrutura
O padrão será aplicado a uma classe Contexto. Os diferentes algorítmos serão
implementados em diferentes classes, mas com uma interface comum (Estratégia) como
mostra a Figura 4.11:
Figura 4.11 – Estrutura do padrão Strategy
4.7.2 Implementação
Pode-se exemplificar a aplicação do Strategy na implementação de script para
visualização em browsers. Apesar da padronização, browsers como Internet Explorer e
Mozilla interpretam scripts de forma diferente. Usa-se o padrão para implementar
diferentes algoritmos de acordo com o browser.
59
Figura 4.12 – Estrutura do Strategy
public class PaginaWeb { private Script script; public PaginaWeb() { if (navigator.name == "IE") { script = new ScriptIE(); } else { script = new ScriptMozilla(); } } public PaginaWeb(Script novoScript) { script = novoScript; } public void desenhaPopUp() { script.implementaPopUp(); } }
5 MAPEAMENTO DE PADRÕES DE ANÁLISE PARA
PADRÕES DE PROJETO
Um dos objetivos do trabalho é a elaboração de mapeamentos de padrões de
análise para padrões de projeto. Isto é, após estudo aprofundado de um padrão de
análise, o seu modelo conceitual é transformado em um diagrama de classes de projeto
utilizando padrões de projeto. Neste capítulo estão descritos alguns mapeamentos que
foram desenvolvidos durante o trabalho.
5.1 Padrão de Análise Transações e Padrão de Projeto Proxy.
O primeiro projeto refere-se ao mapeamento do padrão de análise Transações
para o padrão de projeto Proxy.
O padrão Proxy se aplica ao modelo de Transações quando as transações são
armazenadas em uma base de dados. Muitos clientes da classe Conta acessarão esta para
consultar somente seu atributo saldo. Nestes casos, não é necessário que Conta tenha
referência para as instâncias da classe Lançamento associadas.
Utiliza-se então o padrão Proxy para que as intâncias da classe Lançamento
sejam carregadas e criadas por demanda.
61
Figura 5.1 – Estrutura do Proxy
Um cliente que necessite somente da consulta do saldo de uma conta irá ter
acesso a uma instância da classe ContaProxy. Esta instância irá carregar apenas o
atributo saldo através da camada de persistência para o banco de dados, prorrogando o
carregamento das instâncias de Lançamento somente quando forem necessárias.
Assim a implementação de calcularSaldo fica da seguinte maneira:
Public float calcularSaldo() { If (real == null) { real = new ContaReal(); } return real.calcularSaldo(); }
5.2 Padrão de Análise Conta Resumida e Padrão de Projeto Composite
Este mapeamento toma como base o padrão de análise Conta projetado pelo
padrão Composite.
62
Figura 5.2 – Padrão de Análise Conta Resumida
Figura 5.3 - Padrão de Projeto Composite
O conceito de Conta será implementado como a classe Componente do padrão
Composite. ContaResumida será o objeto composto do padrão, ou seja, o
ComponenteComplexo. ContaDetalhada será o objeto simples, ou a classe
ComponenteSimples. O diagrama de classe do mapeamento é descrito na Figura 5.4.
63
Figura 5.4 – Diagrama de classe do mapeamento
A implementação segue as regras do padrão Composite. Conta é uma classe
abstrata, mas implementa os métodos comuns à ContaDetalhada e à ContaResumida
como obterSaldo. Conta também define o atributo saldo comum as duas classes. Em
ContaResumida há um método particular, calcularSaldo. Este método deve consultar o
saldo de todos os objetos de Conta associados ao objeto ContaResumida, que podem,
pos sua vez, serem contas detalhadas ou contas resumidas. A implementação segue
abaixo.
Public float calcularSaldo() { Conta _conta; While (agrupadas.hasNext()) { _conta = agrupadas.next(); this.saldo = this.saldo + _conta.obterSaldo(); } return saldo; }
64
5.3 Padrão de Análise Portfólio e Padrão de Projeto Strategy
O padrão de análise Portfólio (seção 4.6.2) consiste em uma coleção de contratos
que obedecem a certos critérios de seleção. Uma das maneiras abordadas para a
construção desta coleção é através de um método de seleção que recebe um contrato
como parâmentro. Caso este método retorne um valor verdadeiro, o contrato será
associado ao Portfólio (figura 5.5).
Figura 5.5 – Seleção de contrato por método
A utilização do padrão Strategy para implementação do Método de Seleção do
Portfólio possibilita a utilização de vários métodos de seleção distintos, e a escolha de
que método de seleção será utilizado pelo Portfólio para seleção de contratos pode ser
feita por externamente à classe, dando grande flexibilidade ao sistema.
A seguir, tem-se o diagrama de classes de um Portfólio projetado usando-se
Strategy.
65
Figura 5.6 – Estrutura da implementação
A implementação da classe Contrato fica da seguinte forma:
public class Portfolio { protected MetodoSelecao filtro; public selecionarMetodo(String selecao) { if (selecao == "porValor") { filtro = new SelecaoValor(); } else { filtro = new SelecaoData(); } } public void analisar() { Contrato contrato; while selecao.hasNext() { contrato = selecao.next(); if (filtro.filtrar(contrato) == false) { selecao.remove(contrato); } } }
66
No exemplo acima o método selecionarMetodo recebe um parametro selecao
que define qual método de seleção será usado pela instância de Portfolio.
5.3.1 Uso do Singleton
De acordo com o padrão de análise Portfólio, um mesmo método de seleção
pode ser usado por vários portfólios. Neste caso pode-se evitar o custo da criação de
várias instâncias de MétodoSeleção implementando-se as classes concretas dos métodos
de seleção como Singletons.
Assim tem-se o mapeamento do padrão de análise Portfólio para os padrões de
projeto Singleton e Strategy (figura 5.7).
Figura 5.7 – Portfólio mapeado para Singleton e Strategy
Abaixo segue a implementação da classe SelecaoValor como um Singleton: public class SelecaoValor implements MetodoSelecao { protected SelecaoValor(){} public boolean filtrar(Contrato contrato){ }
67
public static SelecaoValor obterInstancia(){ if (instancia == null) { instancia = new SelecaoValor(); } return instancia; } private static SelecaoValor instancia = null; }
5.4 Padrão de Análise Regra de Lançamento e Padrão Strategy
Em um caso semelhante ao anterior, o padrão de análise Regra de Lançamento
possui um método para cálculo do valor do lançamento que será efetuado na sua Conta
de saída (figura 5.8). Para flexibilizar a classe de Regra de Lançamento, também faz-se
usar do padrão Strategy na sua implementação.
Figura 5.8 – Padrão de análise Regra de Lançamento
Figura 5.9 – Mapeamento do Padrão Regra de Lançamento para o Strategy
68
A implementação da classe RegraLancamento segue abaixo:
public class RegraLancamento { protected Conta saida; protected MetodoCalculo calculo; public alterarSaldo(float saldo) { if (saldo > 1000) { calculo = new MetodoCalculoA(); } else { calculo = new MetodoCalculoB(); } } public void calcular() { saida.lanca(calculo.calcular()); } }
O metodo alterarSaldo de RegraLancamento recebe saldo como parâmetro, e a
partir dele decide que método de cálculo será usado.
6 DESENVOLVIMENTO
O objetivo final do trabalho é desenvolver um modelo de análise e de projeto e
implementar uma aplicação fazendo uso de alguns padrões estudados durante a fase de
pesquisa. Neste capítulo será descrito como os padrões estudados foram utilizados no
processo de desenvolvimento da aplicação de controle de atacado.
6.1 Levantamento de Requisitos
6.1.1 Descrição do Domínio da Aplicação
O domínio desta aplicação é a consignação e venda de produtos. A empresa que
serviu de base para o desenvolvimento é um atacado de jóias. Lá são vendidos vários
tipos de jóias e semijóias, além de relógios, embalagens e mostruários.
A maioria dos clientes da joalheria trabalha com produtos consignados. O
processo de consignação se dá da seguinte maneira: um consignador (cliente da
empresa) vai até a empresa e retira alguns produtos para levar em consignação. Este
vendedor tem um período para vender os produtos (fora da empresa). Após este período
ele retorna à empresa, e realiza o fechamento: faz o pagamento dos produtos que foram
vendidos e faz a devolução dos produtos que não conseguiu vender.
A empresa também faz vendas sem consignação. Ou seja, um cliente vem até a
empresa e faz a compra de um produto diretamente, sem consigná-lo.
Além de controlar a parte de consignação e venda, o software deve fazer o
controle de estoque, fluxo de caixa, e geração relatórios gerais (lucro, gastos, estoque
etc.).
O estoque fica dividido em produtos que estão no estoque da empresa e produtos
consignados.
70
Como fluxo de caixa, o sistema deve registrar os pagamentos aos fornecedores, o
recebimento de contas a prazo (promissórias e cheques) e os gastos administrativos da
empresa.
6.1.2 Casos de Uso
A seguir a descrição dos casos de uso p/ aplicação. Caso de Uso: Consignar Produtos Atores: Funcionário.
Tipo: Primário
Descrição: O cliente escolhe os produtos que deseja levar em consignação. O
funcionário registra os produtos em poder do cliente.
Seqüência Típica de Eventos Ação do Ator Resposta do Sistema
1- O funcionário se identifica através
do código do funcionário.
2- O funcionário identifica o cliente
através do código do cliente.
3- O sistema informa os dados do
cliente (nome, telefones, RG, CPF,
endereço) e os produtos consignados (código
do produto, nome do produto, quantidade),
caso ele tenha alguma.
4- O funcionário identifica os
produtos escolhidos pelo cliente através dos
códigos dos produtos.
Caso haja mais de um produto do
mesmo tipo, o funcionário entra também
com a quantidade.
5- O sistema informa o valor total
dos produtos e o valor mínimo que deve ser
vendido para alcançar a meta de venda.
71
6- O funcionário confirma a
operação.
7- O sistema imprime um relatório
contendo o código e nome do cliente; nome
do funcionário que realizou a operação;
código, nome, quantidade, valor unitário e
valor total dos produtos consignados na
operação.
Seqüências Alternativas
1.1 – O código do funcionário é inválido. Cancelar a operação.
2.1 – O código do cliente é inválido (não está cadastrado). Cancelar a operação e
perguntar ao usuário se deseja cadastrar o cliente (caso de uso Cadastrar Cliente).
4.1 – O código do produto é inválido (não está cadastrada). Perguntar ao usuário
se deseja cadastrar o produto (caso de uso Cadastrar Produto).
Caso de Uso: Fazer Fechamento
Atores: Funcionário.
Tipo: Primário
Descrição: O cliente informa os produtos que foram vendidos e os produtos que
serão devolvidos. O funcionário registra os produtos devolvidos e,
consultando o registro de produtos em poder do cliente, informa o
débito do cliente. Este faz o pagamento à vista ou escolhe formas de
pagamento a prazo.
72
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O funcionário se identifica através
do código do funcionário.
2- O funcionário identifica o cliente
através do código do cliente.
3- O sistema informa os dados do
cliente (nome, telefones, RG, CPF,
endereço) e seus produtos consignados
(código do produto, nome do produto,
quantidade).
4- O funcionário seleciona dentre os
produtos consignados ao cliente, quais serão
devolvidos, caso haja devolução.
5- O sistema informa o valor total
dos produtos devolvidos e o valor total dos
produtos comprados pelo cliente.
5- O funcionário registra a forma de
pagamento escolhida pelo cliente – avista ou
a prazo.
Na forma de pagamento a prazo deve
ser identificado se será usado cheque ou
promissória.
6- Se a forma de pagamento a prazo
for escolhida o sistema mostra o valor das
prestações.
7- O cliente faz o pagamento (se for o
caso) e o funcionário confirma a operação.
8- O sistema imprime um relatório
contendo código e nome do cliente; nome do
funcionário que realizou a operação; código,
nome, quantidade, valor unitário e valor total
dos produtos vendidas na operação.
Seqüências Alternativas
1.1 – O código do funcionário é inválido. Cancelar a operação.
4.1 – O cliente deseja devolver mais produtos que o permitido pela meta de
vendas. O gerente entra com o código do gerente para permitir a devolução.
73
Caso de Uso: Comprar Produtos
Atores: Funcionário
Tipo: Primário
Descrição: O fornecedor é identificado e os produtos são comprados. É feito o
pagamento à vista ao fornecedor, ou escolhe-se formas de
pagamento a prazo.
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O funcionário se identifica através
do código do funcionário.
2- O funcionário identifica o
fornecedor através do código do fornecedor.
3- O sistema informa os dados do
fornecedor (nome, telefones, RG, CPF,
endereço)
4- O funcionário registra os produtos
que serão comprados no sistema, informando
código, descrição e preço de compra.
5- O sistema informa o total da
compra.
6- O funcionário escolhe a forma de
pagamento – à vista ou à prazo.
Na forma de pagamento a prazo deve
ser identificado se será usado cheque ou
promissória (carnê).
7- O sistema imprime um relatório
contendo código e nome do fornecedor;
nome do funcionário que realizou a
operação; código, nome, quantidade, valor
unitário e valor total dos produtos
comprados na operação.
Seqüências Alternativas
1.1 – O código do funcionário é inválido. Cancelar a operação.
2.1 – O código do fornecedor é inválido. Cancelar a operação e dispara caso de
uso Cadastrar Fornecedor.
74
Caso de Uso: Vender Produtos
Atores: Funcionário.
Tipo: Primário
Descrição: O funcionário apresenta os produtos ao cliente. O cliente escolhe os
produtos e faz o pagamento, ou escolhe formas de pagamento a
prazo.
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O funcionário se identifica através
do código do funcionário.
2- O funcionário identifica o cliente
através do código do cliente.
3- O sistema informa os dados do
cliente (nome, telefones, RG, CPF,
endereço) e seus produtos consignados
(código do produto, nome do produto,
quantidade).
4- O funcionário identifica os
produtos escolhidos pelo cliente através do
código do produto.
Caso haja mais de um produto do
mesmo tipo, o funcionário entra também
com a quantidade.
5- O sistema informa o valor total
dos produtos
6- O funcionário registra a forma de
pagamento escolhida pelo cliente – a vista
ou a prazo.
Na forma de pagamento a prazo deve
ser identificado se será usado cheque ou
promissória.
7- Se a forma de pagamento a prazo
for escolhida o sistema mostra o valor das
prestações.
75
8- O cliente faz o pagamento (se for o
caso) e o funcionário confirma a operação.
9- O sistema imprime um relatório
contendo código e nome do cliente; nome do
funcionário que realizou a operação; código,
nome, quantidade, valor unitário e valor total
dos produtos vendidas na operação.
Seqüências Alternativas
1.1 – O código do funcionário é inválido. Cancelar a operação.
2.1 – O código do cliente é inválido (não está cadastrado). Cancelar a operação e
perguntar ao usuário se deseja cadastrar o cliente (caso de uso Cadastrar Cliente).
4.1 – O código do produto é inválido (não está cadastrada). Disparar caso de uso
Cadastrar Produtos.
Caso de Uso: Vender Produtos à Vista
Atores: Funcionário.
Tipo: Primário
Descrição: O funcionário apresenta os produtos ao cliente. O cliente escolhe os
produtos e faz o pagamento a vista. Neste caso de uso o cliente não
é identificado.
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O funcionário identifica-se através
do código do funcionário.
2- O funcionário identifica os
produtos escolhidos pelo cliente através do
código do produto.
Caso haja mais de um produto do
mesmo tipo, o funcionário entra também
com a quantidade.
3- O sistema informa o valor total
dos produtos
76
4- O cliente faz o pagamento (se for o
caso) e o funcionário confirma a operação.
Seqüências Alternativas
2.1 – O código do produto é inválido (não está cadastrada). Disparar o caso de
uso Cadastrar Produtos.
Caso de Uso: Fazer Pagamento ao Fornecedor
Atores: Funcionário
Tipo: Primário
Descrição: O pagamento é feito ao fornecedor e o débito é eliminado
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O funcionário identifica o
fornecedor através do código do fornecedor.
2- O sistema exibe a lista de débitos
com o fornecedor identificado.
3- O funcionário escolhe o
pagamento dentre a lista de débitos.
4- O sistema imprime um recibo
contendo o nome do fornecedor e o valor do
pagamento.
Caso de Uso: Receber Pagamento
Atores: Funcionário
Tipo: Primário
Descrição: O cliente faz o pagamento e seu débito é liquidado.
77
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O funcionário identifica o cliente
através do código do cliente.
2- O sistema exibe a lista de débitos
do cliente identificado (nome do cliente, data
e valor do débito).
3- O funcionário escolhe qual débito
será liquidado.
4- O sistema imprime um recibo
contendo o nome do cliente e o valor do
pagamento.
Seqüências Alternativas
3.1 – O cliente paga um valor inferior ao valor do débito. O gerente entra com o
código do gerente e o débito é alterado com o novo valor (valor da dívida – valor pago).
Caso de Uso: Definir Meta de Venda
Atores: Gerente
Tipo: Secundário
Descrição: O gerente define a porcentagem de produtos consignados que deve
ser vendidas, ou seja, que não podem ser devolvidas.
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O gerente define a porcentagem de
produtos consignados que deve ser vendidas
(não pode ser devolvidas) para todos os
clientes.
2- O gerente confirma a operação
78
Caso de Uso: Fazer Pagamento Administrativo
Atores: Funcionário
Tipo: Secundário
Descrição: O funcionário descreve um gasto administrativo e fornece seu valor.
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O funcionário identifica-se através
do código do funcionário.
2- O funcionário descreve o gasto
administrativo e fornece o valor.
Caso de Uso: Cadastrar Cliente
Atores: Funcionário
Tipo: Primário
Descrição: Cadastra um cliente.
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O funcionário preenche um
cadastro com os dados do cliente (nome,
telefone, RG, CPF, data de nascimento,
endereço)
2- O funcionário confere os dados e
confirma a operação.
79
Caso de Uso: Cadastrar Produto
Atores: Funcionário
Tipo: Primário
Descrição: Cadastra umo produto.
Seqüência Típica de Eventos
Ação do Ator Resposta do Sistema
1- O funcionário preenche um
cadastro com os dados do produto
(descrição, quantidade, preço)
2- O funcionário confere os dados e
confirma a operação.
80
6.2 Análise
Como descreve Craig Larman (1997) “Para criar o software de uma aplicação, é
necessário uma descrição do problema e dos seus requisitos”. A análise nada mais é do
que a transformação dos requisitos da aplicação em especificações que descreverão uma
solução lógica para o domínio do problema.
Nesta fase procura-se pesquisar e entender o domínio do problema, a partir da
descrição dos requisitos, para o desenvolvimento de um modelo conceitual. É o que
Craig Larman (2001) chama de “investigação do problema”.
6.2.1 Modelo Conceitual
A palavra modelo pode ser definida como “uma representação abstrata que
permite descrever e/ou prever comportamentos específicos de um sistema, através do
estudo de um número reduzido de características relevantes do sistema” (MAFFEO,
1992) Esta representação pode ser de um fenômeno do mundo real (as leis físicas de
movimento são um modelo do movimento no mundo real), de uma máquina ou de um
software. Os resultados dos processos de análise serão modelos do domínio da aplicação
que está sendo desenvolvida.
O modelo conceitual “ilustra os conceitos significativos em um domínio de
problema” (LARMAN, 1997). Ele é um resultado da fase de análise e será a base para a
fase de projeto.
A partir da análise dos casos de uso e dos requisitos, decompõe-se o “problema em
conceitos e objetos individuais” (LARMAN, 1997). Nesta parte do desenvolvimento
ocorre a elaboração de um modelo conceitual do problema.
No modelo da análise não se descreve artefatos de implementação de software
como classes de interface com o usuário ou camadas de persistência. O objetivo do
modelo conceitual não é ser um projeto de software, mas uma descrição de coisas do
mundo real que fazem parte do domínio do problema e sua solução.
“Modelos não são certos ou errados, eles são mais ou menos úteis” (FOWLER,
1997). A escolha do modelo terá conseqüências na flexibilidade e reusabilidade do
81
projeto. Por isso, um estudo aprofundado do domínio é importante antes de iniciar a
modelagem.
No modelo conceitual do controle de atacado a classe Atacado representa a
empresa proprietária. Os clientes do atacado são representados pela classe Cliente. Os
fornecedores são representados pela classe Fornecedor. Atacado, Cliente, Fornecedor e
Funcionário são especificações de Entidade, pois representam conceitos de
pessoa/organização (figura 6.1).
Figura 6.1 – Modelo baseado no padrão Entidade
A consignação, compra e venda de produtos, descrita nos casos de uso de Compra
de Produtos, Venda de Produtos e Consignação de Produtos são representadas através
82
das classes Compra, Venda e Consignação. Toda venda e toda consignação são feitas do
atacado para um cliente. Então associou-se o Cliente à Consignação e à Venda. Uma
compra é sempre feita de um fornecedor. Assim associou-se Compra à Fornecedor
(figura 6.2).
Figura 6.2 – Modelo sem conceito de Contrato
Nota-se que as classes de operação Venda, Consignação e Compra estão
associadas sempre à duas entidades, sendo uma das entidades o Atacado. Assim sendo,
baseou-se no padrão Contratos, que diz que “Um Contrato pode ser uma compra, uma
venda ou qualquer outra transação feita entre entidades”, para generalizar as três
operações em uma classe Contrato como descrito na figura 6.3.
83
Figura 6.3 – Conceito de Contrato
A data_fechamento de Consignação é a data em que o cliente devolve os produtos
que levou consignado como descrito no caso de uso consignar produtos.
A classe Contrato está associada ao Atacado, pois as operações de compra, venda
e consignação sempre tem o atacado como parte contratada. Contrato também é
associado a um Funcionário, o qual irá iniciar o contrato no sistema. Finalmente tem-se
a associação de Contrato com Produto, pois as operações realizam a comercialização de
produtos do atacado (figura 6.4).
84
Figura 6.4 – Modelo com conceito de Contrato
O padrão Conta Resumida foi utilizado para elaborar o controle de caixa do
sistema. A classe Caixa, uma Conta Resumida, irá representar o caixa da empresa, tendo
como Contas Detalhadas a conta Receita e a conta Despesa. Lançamentos feitos em
Receita representam entrada de dinheiro na empresa. Lançamentos feitos em Despesa
representam os gastos. O atributo saldo de Caixa será diferença entre as receitas e as
despesas da empresa.
85
Figura 6.5 – Conta Caixa, Receita e Despesa
Toda Venda e toda Compra gerará um ou mais Lançamentos. A classe Atacado
também pode gerar Lançamentos que serão referentes a despesas administrativas como
pagamentos de salário, compra de material de escritório, etc. (figura 6.6).
86
Figura 6.6 – Lançamentos nos Caixas
Em Lançamento o atributo valor representa o valor do lançamento em reais. O tipo
de lançamento pode ser cheque, promissória ou dinheiro. A data_lançamento será a data
em que o lançamento será contabilizado no sistema. A data_registro é a data em que o
lançamento foi inserido no sistema. Por exemplo, um pagamento com cheque para 30
dias feito no dia 1º de maio será registrado como um Lançamento do tipo cheque com
data_registro 01/05 e data_lançamento 31/05.
O modelo completo está descrito na figura 6.7.
88
6.3 Projeto
O projeto do software é a definição de quais artefatos de software serão usados
na implementação de uma solução lógica para o problema. E “como eles (os artefatos)
irão colaborar para preencher todos os requisitos” (LARMAN, 2001).
Os artefatos do projeto serão criados com base na inverstigação feita na fase de
análise.
“Seu projeto precisa ser específico para resolver o problema, mas também
suficientemente flexível para tratar problemas e requisitos que surgirem no futuro”
(GAMMA, 1994). Projetistas iniciantes dificilmente conseguem um bom projeto de
software. Um projeto ruim precisa ser alterado toda vez que surge um novo requisito, ou
se encontra algum problema.
Projetistas exprientes conseguem fazer um bom projeto utilizando suas
experiências anteriores. Quando um problema de projeto é solucionado, esta solução é
registrada, para que no futuro, quando surgir um problema igual ou semelhante, não seja
necessário dispender tempo desenvolvendo um novo projeto. Assim nasce um padrão de
projeto.
6.3.1 Linguagem e Banco de Dados
A linguagem escolhida para o desenvolvimento da aplicação foi Java. Os fatores
que influenciaram na escolha foram sua natureza multi-plataforma, robustez e relativa
facilidade de programação (quando comparada com C++).
Como a aplicação a ser desenvolvida utiliza objetos persistentes, fez-se
necessário o estudo de um sistema gerenciador de banco de dados (SGBD).
Um fator preponderante na escolha do SGBD foi a portabilidade, pois a
aplicação desenvolvida utiliza linguagem Java, que tem portabilidade como uma das
caracterísiticas principais.
Outro fator foi o custo do software. Os SGBDs mais populares normalmente
exigem altos custos para sua aquisição. Neste trabalho pretende-se desenvolver uma
aplicação de baixo custo, que poderá ser utilizada em um sistema operacional gratuito,
89
como Linux. Isso possibilita que os usuários da aplicação arquem com os custos apenas
de equipamento e treinamento.
Levando em conta os fatores descritos acima, escolheu-se o Firebird como
SGBD da aplicação a ser desenvolvida. O Firebird é um banco de dados relacional de
código aberto que deriva do Interbase. O Interbase também é um SGBD freeware
pertencente a Borland. Apesar de ser freeware, o Interbase é um projeto com direções
mais comerciais.
O Firebird se mostrou um banco de dados estável. Oferece suporte a triggers e
stored procedures. A versão usada foi a 1.5 ClassicServer (IBPHOENIX, 2004).
A instalação do Firebird não inclui interface gráfica para manipulação de dados.
Mas interfaces são encontradas facilmente na Internet, principalmente porque a maioria
das interfaces para Interbase são compatíveis com o Firebird.
Iniciou-se o trabalho usando o IBAccess, que se mostrou muito limitada e
instável. Na procura de uma interface mais robusta, encontrou-se o IBExpert, que
mesmo na versão mais simples (Personal) possui vários recursos que ajudam a tirar
proveito de todas as funcionalidades do banco de dados Firebird.
6.3.2 Diagramas de Sequência
Das ferramentas UML para descrição de um projeto de software, duas serão
utilizadas para o desenvolvimento da aplicação de Controle de Estoque: os diagramas de
sequência que descreve “como os objetos interagem via menssagens” (LARMAN,
2001) e o diagrama de classe, que descreve as classes do projeto e suas associações.
Dando continuidade a fase de projeto, foram criados diagramas de interação para
os casos de uso correspondentes. Os diagramas de interação mostram “como os objetos
interagem através de mensagens para cumprir tarefas.” (LARMAN, 1997).
Os diagramas de interação podem ser representados através de dois tipo distintos:
diagramas de colaboração, que representam a interação entre os objetos do software
através de um grafo, e diagramas de sequência. A seguir tem-se os diagramas de
sequência para os casos de uso mais relevantes do sistema.
90
6.3.2.1 Consignar
O diagrama de sequência de consignar que representa o caso de uso Consignar
Produto é descrito na Figura 6.8.
Figura 6.8 – Diagrama de Sequência Consignar
91
6.3.2.2 Vender
Diagrama de Sequência do Caso de Uso Vender Produto descrito na Figura 6.9.
Figura 6.9 – Diagrama de Sequência Vender
92
6.3.3 Diagrama de Classes
Em paralelo com a criação dos diagramas de colaboração é feita a elaboração do
diagrama de classes de projeto. Utilizando como base os métodos e relações
encontrados nos diagramas de sequência, o diagrama de classes vai descrever as classes
do projeto, seus métodos, atributos e associações. Diferente do modelo conceitual, o
diagrama de classes “mostra definições de classes de software em vez de conceitos do
mundo real” (LARMAN, 2001).
6.3.3.1 Contratos
No projeto a classe Contrato foi implementada como abstrata. Esta classe
implementa o comportamento comum de suas subclasses Venda, Compra e
Consignação. A classe Contrato também possui uma associação com Produtos. Ao
carregar um objeto de Contrato do banco de dados, todas as instâncias de Produtos
associadas a este Contrato serão carregadas e instânciadas. Isso cria um problema de
desempenho caso várias instâncias de Contrato precisem ser carregadas, por exemplo,
para geração de relatórios.
Para solucionar este problema, foi utilizado o padrão Proxy na implementação
de Contrato e suas subclasses. Assim duas classes podem representar um contrato:
ContratoReal e ContratoProxy. A classe ContratoProxy não carrega as instâncias de
Produto na sua criação, eliminando o problema de desempenho nas operações que
necessitam consultar apenas propriedades da classe Contrato. Uma interface Contrato
foi criada para interfacear as classes ContratoReal e ContratoProxy.
93
Figura 6.10 – Contrato com Proxy
O padrão Proxy descrito por Gamma (1994) não prevê herança no uso do Proxy.
Como Contrato tem subclasses, houve a necessidade de estender o padrão para estas
classes. Para isso aplicou-se o padrão as subclasses de Contrato. Assim cada
participante de Proxy especializa a superclasse que representa o mesmo papel, como
segue abaixo:
94
Figura 6.11 – Heranças de contrato com Proxy
6.3.3.2 Conta Caixa
Como o padrão de análise Conta Resumida (seção 3.2.4) foi aplicado para
modelar o controle de caixa, e no mapeamento de padrões de análise para padrões de
projeto desenvolveu-se o mapeamento do padrão Conta Resumida para o padrão
Composite (seção 6.2), projetou-se o controle de caixa fazendo uso desse mapeamento.
95
Figura 6.12 – Conta Resumida projetada para Caixas
Assim Caixa é uma ContaResumida que irá agrupar uma instância de Receita e
uma instância de Despesa. Obedecendo ao padrão Composite todas as classes
implementam uma interface comum, que neste caso é a interface Conta.
Como neste caso a empresa terá apenas um caixa, implementou-se a classe
Caixa como um Singleton. O atributo caixa é implementado como atributo de classe e o
método obterCaixa é implementado como métodos de classe (static). O mesmo é valido
para as classes Receita e Despesa.
96
6.3.3.3 Camada de Persistência
Como a aplicação utiliza-se de banco de dados para tornar seus objetos
persistentes, houve a necessidade de se implementar uma camada de persistência.
A camada de persitência desenvolvida consiste de uma coleção de mapeadores
de classe. Cada classe persistente do sistema possui um mapeador na camada de
persistência. O mapeador tem as informações de como uma instância da classe é
armazenada no banco de dados, e implementa as querys SQL para armazenar um objeto
no banco, carrega-lo no sistema, fazer alterações, etc.
Todos os mapeadores tem uma interface comum, com métodos como
carregar(int id), salvar(Object objeto), existe(int codigo).
Seguindo o padrão Facade (seção 5.6), criou-se uma classe que serve de
interface de comunicação da camada de aplicação com todos os mapeadores da camada
de persistência. Esta classe, chamada de Persistência, também é responsável pela
criação da conexão com o sistema de banco de dados. Caso o sistema de banco de dados
da aplicação seja alterado, a classe Persistência é a única a sofrer alteração na sua
implementação.
Como a aplicação possui somente um banco de dados, a classe Persistência foi
implementada como um Singleton.
98
6.4 Implementação
Com os artefatos UML gerados na fase de projeto, tem-se uma base para o início
da fase de implementação da aplicação. Nesta fase, os artefatos UML são transformados
em código de alguma linguagem de programação.
Para implementação desta aplicação a linguagem de programação escolhida foi
Java. O ambiente de programação utilizado foi o NetBeans IDE 3.5.1.
A seguir serão detalhados alguns métodos que merecem atenção.
6.4.1 Método Vender
O caso de uso vender é implementado pelo método vender da classe Atacado.
Baseando-se no diagrama de sequência, este método recebe como parâmetro o código
do funcionário, código do cliente, um vetor com os códigos dos produtos vendidos, um
vetor com as quantidades de cada produto e o número de produtos. Os parâmetros
código do funcionário e código do cliente não serão utilizados no protótipo. Abaixo
segue a assinatura do método:
Public void vender(int codigoFun, int codigoCliente, int[] codigoProdutos, int[] quantidades, int Produtos)
O método vender carrega as informações do banco de dados através da
referência que atacado têm à classe Persistência. Através do método carregar, todos os
produtos cujo código está no vetor codigoProdutos serão carregados para uma instância
da classe Vector (classe da biblioteca Java que implementa um array dinâmico).
Vector vetorProdutos = bd.carregar(codigoProdutos, Produto.class, nProdutos);
Após o carregamento, é executado o método vender para todas as instâncias da
classe Produto armazenadas em vetorProdutos. O método vender, da classe Produto,
recebe como parâmetro a quantidade de produtos vendidos. Este método diminui a
quantidade passada como parâmetro do atributo quantidade da instância (faz a retirada
99
do estoque), e retorna uma instância clone do produto.O valor do atributo quantidade
deste clone tem o mesmo valor do parâmetro quantidade passado para o método (o
clone representa o produto vendido). Abaixo segue o método vender da classe Produto.
public Produto vender(int qtd) { Produto prodVendido; prodVendido = this.clonar(); prodVendido.quantidade = qtd; this.quantidade = this.quantidade - qtd; return prodVendido; }
Os produtos fornecidos pelo método vender, da classe Produto, são armazenados
em uma instância da classe Vector, chamada vetorProdVendidos. Esse vetor é passado
como argumento para uma instância da classe VendaReal, que vai registrar a operação
de venda. Essa instância de VendaReal é então salva no banco de dados. Os produtos os
quais as quantidades foram alteradas (instâncias de Produto as quais o método vender
foi executado) também são salvos no banco.
VendaReal venda = new VendaReal( 0, 0, vetorProdVendido); bd.salvar(venda); bd.salvar(vetorProdEstoque);
6.4.2 Método obterVendas
O método obterVendas da Classe atacado carrega todas as vendas registradas no
banco de dados. Para melhorar o desempenho, este método faz uso de objetos Proxy de
Vendas (ver subseção 6.3.3.1) para carregar os objetos do banco de dados.
public Vector obterVendas() { return bd.carregar(VendaProxy.class); }
100
Caso sejam requisitados os produtos de alguma instância de Vendas, a instância
Proxy fará o carregamento da instância real, como foi explicado no padrão Proxy
(subcapítulo 4.3). Segue a implementação do método obterProduto na classe
VendaProxy.
public Vector obterProdutos() { vendaReal = (VendaReal)bd.carregar(this.codigoContrato, VendaReal.class); return vendaReal.obterProdutos(); }
6.4.3 Camada de Persistência
A camada de persistência foi elaborada para que a aplicação ganhe em
estendibilidade e portabilidade. Assim foi criada uma classe Persistência, que servirá de
Fachada (ver sub capítulo 4.4, padrão Facade) para toda a camada de persistência, que
se constitui dos mapeadores das classes da camada de aplicação (ver subseção 6.3.3.3).
Para salvar um objeto, primeiramente a instância de Persistência define que
mapeador será usado. Isso é feito através do método retornarMapeador que recebe uma
classe como parâmetro e retorna uma instância da classe Mapeador. A classe que é
passada como argumento para o método retornarMapeador é a classe do parâmetro do
método salvar.
Para carregar um objeto, o sistema deve informar para a instância de Persistência
a classe do objeto que será carregado, e também um atributo identificador do objeto. A
implementação dos métodos salvar, carregar e retornarMapeador segue abaixo:
public void salvar(Object objeto) { Mapeador mapeador; mapeador = this.retornarMapeador(objeto.getClass()); mapeador.salvar(objeto); }
101
public Object carregar(int codigo, Class classe) { Mapeador mapeador; mapeador = this.retornarMapeador(classe); return mapeador.carregar(codigo); } public Mapeador retornarMapeador(Class classe) { Mapeador map; map = null; if (classe == Produto.class) { map = new MapeadorPeca(conn); } else { if (classe == VendaReal.class) { map = new MapeadorVendaReal(conn); } } . . . return map; }
6.4.4 Protótipo
Tendo uma parte da aplicação implementada, construiu-se uma simples interface
para demonstração do protótipo (figura 6.14). A interface limita-se a verificar o estoque,
realizar vendas, e obter as vendas feitas pelo sistema (utilizando o método obterVendas
para demonstração da aplicação do Proxy).
102
Figura 6.14 - Protótipo da Aplicação
6.4.4.1 Funcionamento do Protótipo
O protótipo inicia com um menu inicial de 3 opções. Estoque, Vender e
Consultar Vendas. Em Estoque pode-se conferir o estoque de produtos registrados, seu
código, seu preço e sua quantidade (figura 6.15).
Em Vender pode se efetuar uma venda de produtos. Na primeira coluna coloca-
se o código do produto, e ao lado, a sua quantidade. Clicando em Vender, a Venda é
efetuada. A quantidade dos produtos vendidos é deduzida dos produtos cadastrados no
estoque, e a venda é registrada no banco de dados (figura 6.16).
Em Consultar Vendas, as vendas realizadas são mostradas na tabela de vendas.
Selecionando uma venda na tabela e clicando no botão Detalhar Vendas, são mostrados
os produtos associados a respectiva venda na tabela inferior (figura 6.17).
103
Figura 6.15 – Consultando o estoque
Figura 6.16 – Realizando a venda
Figura 6.17 – Consultando a venda realizada
7 CONCLUSÃO
Através deste estudo, pode-se comprovar o grande potencial do uso dos padrões
no desenvolvimento de software. O domínio do problema escolhido para a construção
de uma aplicação, que demonstrasse os benefícios do uso de padrões, era relativamente
simples. Mesmo assim pode-se constatar ganhos em estendibilidade, desempenho,
reusabilidade e portabilidade.
7.1 Fatores de Qualidade Alcançados
7.1.1 Estendibilidade
Estendibilidade é a capacidade que a aplicação tem de se adaptar a novos
requisitos. Quanto menor for o esforço utilizado em uma alteração de requisitos, maior a
estendibilidade do sistema.
Pode-se comprovar a estendibilidade da aplicação de Controle de Atacado no
uso do padrão Contratos. A adição de uma nova atividade na empresa que envolva
contratos, exige apenas a adição de uma subclasse de Contratos, poupando trabalho
tanto do projetista como do programador.
Por exemplo, caso fosse adicionado o seguinte requisito: “o sistema deve
registrar o aluguel de produtos”, uma classe Aluguel, subclasse de Contrato, seria
adicionada ao projeto.
O uso do padrão de análise Conta, projetado usando-se o padrão Composite,
também aprimora a estendibilidade da aplicação, permitindo que se adicione um número
maior de componentes da classe Conta ao sistema. Pode-se facilmente implementar um
maior controle de contabilidade no sistema, adicionando, por exemplo, contas como:
despesa com material, despesa com funcionário, despesa com impostos, etc.
105
7.1.2 Desempenho
O desempenho de uma aplicação tem grande impacto na aceitação da aplicação
pelo usuário. Uma aplicação que não soluciona os problemas com velocidade perde um
pouco de sua aplicabilidade.
O ganho de desempenho é claramente demonstrado com o uso do padrão Proxy
na construção das subclasses de Contrato. No atacado usado como base para a
construção da aplicação, cada operação de consignação registra uma média de 80
produtos. O atacado tem cerca de 100 clientes que realizam 3 consignações por mês.
Assim uma consulta de todas as consignações do mês, sem a utilização do Proxy
resultaria no carregamento de 24000 objetos do banco de dados. Considerando que o
referido atacado é uma microempresa, o número pode crescer para valores inaceitáveis.
7.1.3 Reusabilidade
Todos os padrões utilizados na análise e no projeto são genéricos o bastante para
serem utilizados mais de uma vez. Reusabilidade é uma característica inata a um
software que foi analisado e projetado usando padrões.
7.1.4 Portabilidade
A aplicação desenvolvida tem um de seus pontos fortes na portabilidade, não só
por ser implementada numa linguagem multi-plataforma, mas o uso do padrão Facade
para implementação da camada de persistência torna toda a camada de aplicação
independente do sistema gerenciador de banco de dados.
A aplicação pode ser portada de um PC usando Windows como sistema
operacional e Oracle como banco de dados, para um computador usando Linux e
MySQL como banco de dados com pequenas alterações na classe Persistência.
106
7.2 Considerações Finais
Analisando os resultados, pode-se concluir que a utilização de padrões favorece
vários aspectos de qualidade de um software. Esta qualidade que possibilita as
constantes atualizações promovidas pelas grandes empresas de software, possibilita que
os erros de aplicações extremamente complexas sejam rapidamente detectados e
corrigidos e torna possível a portabilidade das aplicações num mundo onde a variedade
de sistemas e plataformas crescem a cada dia.
É imprescindível que softwares de médio e grande porte tenham sua análise e
seu projeto baseado em padrões, para tornar economicamente viável a uma
softwarehouse, operações como manutenção e atualização de seus programas.
Por outro lado, o uso dos padrões ainda sofre resistência por parte de analistas e
projetistas, principalmente iniciantes, pois a primeira vista, padrões se demonstram
confusos e sem utilidade. Iniciantes não conseguem visualizar os benefícios que um
esforço extra nas fases de análise e projeto irão trazer num futuro próximo.
Na grande maioria dos casos, a economia de trabalho que um padrão promove é
muito superior ao esforço extra que se faz na pesquisa e implementação do mesmo.
107
BIBLIOGRAFIA CONSULTADA
FOWLER, Martin. Analysis patterns: reusable object models. Addison-Wesley, 1997
GAMMA, Erich; HELM, Richard; JOHNSON, Ralph; et al. Design patterns: elements
of reusable object-oriented software. Reading, Massachusetts: Addison-Wesley, 1994.
LARMAN, Craig. Applying UML and patterns: an introduction to object-oriented
analysis and design and the Unified Process. 2. ed. Upper Saddle River, USA: Prentice
Hall PTR, 2001.
LARMAN, Craig. Utilizando UML e padrões: uma introdução à análise e ao projeto
orientados a objetos. Tradução de Luiz A. Meirelles Salgado, 1. ed. Porto Alegre:
Bookman, 2000.
KRUCHTEN, Philippe. The Rational Unified Process, an introduction. 2. ed.
Addison-Wesley, 2000.
MAFFEO, Bruno. Engenharia de Software e Especificação de Sistemas. Rio de
Janeiro: Campus, 1992.
SOMMERVILLE, Ian. Engenharia de Software. 6. ed. Addison-Wesley, 2003
SILBERSCHATZ, Abraham; KORTH, Henry. F; SUDARRSHAN, S. Sistema de
Banco de Dados. 3. ed. Makron Books, 1999.
108
IBPHOENIX. What is Firebird? Disponível em <http://www.ibphoenix.com>. Acesso
em: 4 de Janeiro de 2004.
SUN. Java 2 Platform, Standard Edition, v 1.4.2 API Specification. Disponível em
<http://java.sun.com/j2se/1.4.2/docs/api>. Acesso em: 20 de Dezembro de 2004.
111
Aplicações de Padrões de Análise e Padrões de Projeto no Desenvolvimento de uma Aplicação de Controle de Atacado
Igor Ghisi
Ciências da Computação
INE – Departamento de Informática e Estatística Universidade Federal de Santa Catarina (UFSC), Brasil, 88040-900
O trabalho “Uso de Padrões de Análise e Padrões de Projeto no Desenvolvimento de uma Aplicação de Controle de Atacado” apresenta um conceito bastante novo no processo de desenvolvimento de software, o conceito de padrão.
Um padrão é uma solução para um determinado tipo de problema. A diferença desta solução está na forma de como ela é descrita. Sua descrição, mais geral e estruturada, favorece a sua utilização em problemas pertencentes a um mesmo contexto. Problemas cujo núcleo da solução seja semelhante.
Os padrões de análise descritos no trabalho são soluções de análise – um modelo conceitual orientado a objeto – para certos domínios de aplicação.
Já os padrões de projeto descrevem soluções de projeto e implementação para problemas de projeto de software. Os padrões descritos foram retirados do livro Design Patterns de Erich Gamma, considerado a Bíblia dos padrões de projeto de software.
No trabalho, foi sugerido um mapeamento de alguns padrões de análise para padrões de projeto, ou seja, formas de projeto e implementação para
os modelos conceituais descritos no padrão de análise. Assim, os domínios de aplicação cuja solução se encaixa em um dos padrões de análise mapeados, já terão uma solução definida para seu projeto.
Para comprovar a eficiência do uso dos padrões, foi desenvolvida uma aplicação usando-se um domínio comum. A aplicação era basicamente um sistema de controle de estoque e controle de caixa. Foi descrito todo o processo de desenvolvimento da aplicação. Nas partes do desenvolvimento onde foram utilizados padrões, foi explicado como e porque tais padrões eram utilizados.
Os benefícios e dificuldades do uso dos padrões foram demonstrados ao fim do trabalho, dando ênfase ao fator estendibilidade que os padrões agregaram a aplicação desenvolvida. O trabalho destaca que o tempo gasto no estudo e na aplicação dos padrões leva a um maior índice de reuso dos artefatos de desenvolvimento de software, inclusive o código de programação, concluindo como válida a aplicação de padrões para qualquer processo de desenvolvimento de software.
115
public class Atacado extends Entidade{ private Persistencia bd; /** Creates a new instance of Atacado */ public Atacado() { bd = Persistencia.obterPersistencia(); } public void vender(int codigoFun, int codigoCliente, int[] codigoProdutos, int[] quantidades, int nProdutos) { Vector vetorProdutos; vetorProdutos = bd.carregar(codigoProdutos, Produto.class, nProdutos); Vector vetorProdVendido = new Vector(); Vector vetorProdEstoque = new Vector(); for (int i = 0; i<vetorProdutos.size(); i++) { Produto produtoEstoque = (Produto)vetorProdutos.get(i); Produto produtoVendido = produtoEstoque.vender(quantidades[i]); vetorProdVendido.add(produtoVendido); vetorProdEstoque.add(produtoEstoque); } VendaReal venda = new VendaReal( 0, 0, vetorProdVendido); bd.salvar(venda); bd.salvar(vetorProdEstoque); //FAZER a Venda } protected void cadastrarProduto(int codigo, String descricao, double preco) { if (!(bd.existe(codigo, Produto.class))) { Produto novoProduto = new Produto(codigo, descricao, preco); bd.salvar(novoProduto); } }
116
public void comprarProduto(int codigo, int quantidade) { Produto prod = (Produto)bd.carregar(codigo, Produto.class); prod.quantidade = prod.quantidade + quantidade; bd.salvar(prod); } public VendaReal carregarVenda(int codigoVenda) { return (VendaReal)bd.carregar(codigoVenda, VendaReal.class); } public Vector obterEstoque() { return bd.carregar(Produto.class); } public Vector obterVendas() { return bd.carregar(VendaReal.class); } private Vector carregarProdutos(int[] codigoProdutos) { Vector vetorProduto = new Vector(); return bd.carregar(codigoProdutos, Produto.class, 1); } private void salvarProdutos(Vector vetorProdutos) { }
117
public interface Contrato { void gerarLancamento(double valor, String tipo, Date data_vencimento); //Lancamento[] fornecerLancamentos(); double calcularValorTotal(); Vector obterProdutos(); } public abstract class ContratoProxy implements Contrato { protected int codigoContrato; protected Date data; protected float valorTotal; /** Creates a new instance of ContratoProxy */ public ContratoProxy() { } public double calcularValorTotal() { return valorTotal; } public void gerarLancamento(double valor, String tipo, Date data_vencimento) { } protected void incrementarTotal(int valor) { } }
118
public class ContratoReal implements Cloneable, Contrato { /** * @clientCardinality 1 * @supplierCardinality 1..* * @label lancamentos*/ //private Lancamento lnkLancamento; protected int codigoContrato; protected Date dataInicio; protected Date dataFim; protected double valorTotal; protected Vector vetorProduto; /** Creates a new instance of Contrato */ public ContratoReal() { } /* public Lancamento[] fornecerLancamentos() { } */ public Vector obterProdutos() { return vetorProduto; } public double calcularValorTotal() { Iterator itProduto = vetorProduto.iterator(); while (itProduto.hasNext()) { Produto prod = (Produto)itProduto.next(); this.valorTotal = this.valorTotal + (prod.obterPreco() * prod.quantidade); } return valorTotal; } public void gerarLancamento(double valor, String tipo, Date data_vencimento) { } protected void incrementarTotal(int valor) { } public ContratoReal obterClone() { ContratoReal oClone = null; try { oClone = (ContratoReal)super.clone(); }
119
catch (Exception e) { System.out.println(e.getMessage()); } return oClone; } } public abstract class Entidade { protected String Nome; protected String Telefone1; protected String Telefone2; protected String email; /** Creates a new instance of Entidade */ public Entidade() { } public String obterNome() { return Nome; } } public interface Mapeador { Object carregar(int codigo); public Vector carregarTudo(); void salvar(Object objeto); void excluir(int codigo); boolean existe(int codigo); } public class MapeadorProduto implements Mapeador { public static Mapeador map = null; protected Connection conn; /** Creates a new instance of MapeadorProduto */ private MapeadorProduto(Connection _conn) { this.conn = _conn; try {
120
conn.setAutoCommit(false); } catch (Exception e) { System.out.println(e.getMessage()); } } public static Mapeador obterMapeador(Connection _conn) { if (map == null) { map = new MapeadorProduto(_conn); } return map; } public Object carregar(int codigo) { Produto prod; prod = new Produto(); try { Statement trans = conn.createStatement(); ResultSet result = trans.executeQuery(sqlSelectCodigo(codigo)); result.next(); prod.codigo = result.getInt("PR_CODIGO"); prod.preco = result.getDouble("PR_PRECO"); prod.descricao = result.getString("PR_DESCRICAO"); prod.quantidade = result.getInt("PR_QUANTIDADE"); } catch (Exception e) { System.out.println(e.getMessage()); } return prod; } public void excluir(int codigo) { int i =0; } public boolean existe(int codigo) { return false; } public void salvar(Object objeto) { Produto prod;
121
prod = (Produto)objeto; int codigo = prod.codigo; try { Statement trans = conn.createStatement(); ResultSet result = trans.executeQuery(sqlSelectCodigo(codigo)); if (result.next()) { trans.executeUpdate(this.sqlUpdate(codigo,prod.quantidade, prod.descricao , prod.preco)); } else { trans.executeUpdate(this.sqlInsere(codigo,prod.quantidade, prod.descricao , prod.preco)); } } catch (Exception e) { System.out.println(e.getMessage()); } try { conn.commit(); } catch (Exception e) { System.out.println(e.getMessage()); } } public Vector carregarTudo() { Vector vetorProduto = new Vector(); Produto prod; try { Statement trans = conn.createStatement(); ResultSet result = trans.executeQuery(sqlSelect()); while (result.next()) { prod = new Produto(); prod.codigo = result.getInt("PR_CODIGO"); prod.preco = result.getDouble("PR_PRECO"); prod.descricao = result.getString("PR_DESCRICAO"); prod.quantidade = result.getInt("PR_QUANTIDADE");
122
vetorProduto.add(prod.clonar()); } } catch (Exception e) { System.out.println(e.getMessage()); } return vetorProduto; } public class MapeadorVendaReal implements Mapeador { private static Mapeador map = null; protected Connection conn; /** Creates a new instance of MapeadorVenda */ private MapeadorVendaReal(Connection _conn) { this.conn = _conn; } public Object carregar(int codigo) { VendaReal venda; venda = new VendaReal(); try { Statement trans = conn.createStatement(); ResultSet result = trans.executeQuery("SELECT * FROM venda WHERE VE_CODIGO = " + String.valueOf(codigo)); result.next(); venda.codigoContrato = result.getInt("VE_CODIGO"); } catch (Exception e) { System.out.println(e.getMessage()); } venda.vetorProduto = this.carregarProdutos(venda.codigoContrato); return venda; } public Vector carregarTudo() { Vector vetorVenda = new Vector(); VendaReal venda; try { Statement trans = conn.createStatement();
123
ResultSet result = trans.executeQuery(sqlSelectAll()); while (result.next()) { venda = new VendaReal(); venda.codigoContrato = result.getInt("VE_CODIGO"); venda.valorTotal = result.getDouble("VE_VALOR"); vetorVenda.add(venda.obterClone()); } } catch (Exception e) { System.out.println(e.getMessage()); } return vetorVenda; } public void salvar(Object objeto) { VendaReal venda; venda = (VendaReal)objeto; int codigoVenda = this.carregeMaxCodigoVenda() + 1; int codigo = venda.codigoContrato; try { Statement trans = conn.createStatement(); ResultSet result = trans.executeQuery(sqlSelectCodigo(codigo)); if (result.next()) { //trans.executeQuery(this.sqlUpdate(codigo,venda.valorTotal)); } else { trans.executeUpdate(this.sqlInsere(codigoVenda, venda.valorTotal)); } } catch (Exception e) { System.out.println(e.getMessage()); } try { conn.commit(); } catch (Exception e) { System.out.println(e.getMessage()); } Vector produtos = venda.obterProdutos(); this.salvarProdutos(codigoVenda, produtos);
124
} public void excluir(int codigo) { } public boolean existe(int codigo) { return true; } public static Mapeador obterMapeador(Connection _conn) { if (map == null) { map = new MapeadorVendaReal(_conn); } return map; } private void salvarProdutos(int codigoVenda, Vector produtos) { try { Iterator itProdutos = produtos.iterator(); while (itProdutos.hasNext()) { Produto prod = (Produto)itProdutos.next(); Statement trans = conn.createStatement(); trans.executeUpdate(this.sqlInsereVendaProd(codigoVenda, prod.codigo, prod.quantidade)); conn.commit(); } } catch (Exception e) { System.out.println(e.getMessage()); } } private Vector carregarProdutos(int codigoVenda) { Vector vetorProdutos = new Vector(); Produto prod = null; Persistencia bd = Persistencia.obterPersistencia(); try { Statement trans = conn.createStatement();
125
vetorProdutos.add(prod.clonar());
ResultSet result = trans.executeQuery("SELECT PR_CODIGO, VE_PR_QUANT FROM venda_produto WHERE VE_CODIGO = "+ String.valueOf(codigoVenda)); while (result.next()) { int codigo = result.getInt("PR_CODIGO"); prod = (Produto)bd.carregar(codigo, Produto.class); prod.quantidade = result.getInt("VE_PR_QUANT");
} } catch (Exception e) { System.out.println(e.getMessage()); } return vetorProdutos; } private int carregeMaxCodigoVenda() { int max = 0; try { Statement trans = conn.createStatement(); ResultSet result = trans.executeQuery("SELECT max(VE_CODIGO) FROM venda"); result.next(); max = result.getInt(1); } catch (Exception e) { System.out.println(e.getMessage()); } return max; } public class Persistencia { private static Persistencia single; protected Connection conn; private Persistencia() { try { Class.forName("org.firebirdsql.jdbc.FBDriver");
126
DriverManager.registerDriver(new org.firebirdsql.jdbc.FBDriver()); } catch (Exception e) { System.out.println(e.getMessage());} try { String databaseURL = "jdbc:firebirdsql:localhost/3050:c:/projetos/sicefree/sicefree.GDB"; this.conn = DriverManager.getConnection(databaseURL, "sysdba", "masterkey"); } catch (Exception e) { System.out.println(e.getMessage()); } } public static Persistencia obterPersistencia() { if (single == null) { single = new Persistencia(); } return single; } public Object carregar(int codigo, Class classe) { Mapeador mapeador; mapeador = this.retornarMapeador(classe); return mapeador.carregar(codigo); } public Vector carregar(int[] codigos, Class classe, int nObjetos) { Vector vetor = new Vector(); int tam = Array.getLength(codigos); for (int i = 0; i<nObjetos; i++) { vetor.add(this.carregar(codigos[i],classe)); } return vetor; }
127
public Vector carregar(Class classe) { Mapeador mapeador; mapeador = this.retornarMapeador(classe); return mapeador.carregarTudo(); } public void salvar(Object objeto) { Mapeador mapeador; mapeador = this.retornarMapeador(objeto.getClass()); mapeador.salvar(objeto); } public void salvar(Vector vetorObjetos) { Mapeador mapeador; Object objeto = vetorObjetos.firstElement(); mapeador = this.retornarMapeador(objeto.getClass()); Iterator itObjetos = vetorObjetos.iterator(); while (itObjetos.hasNext()) { mapeador.salvar(itObjetos.next()); } } public void excluir(Object objeto) { } public boolean existe(int codigo, Class classe) { return false; } public Mapeador retornarMapeador(Class classe) { Mapeador map; map = null; if (classe == ConsignacaoReal.class) { map = new MapeadorConsignacaoReal(conn); }
128
if (classe == Produto.class) { map = MapeadorProduto.obterMapeador(conn); } if (classe == VendaReal.class) { map = MapeadorVendaReal.obterMapeador(conn); } return map; } }
129
public class Produto { protected double preco; protected int codigo; protected int quantidade; protected String descricao; public Produto() { } public Produto(int _codigo, String _desc, double _preco) { this.codigo = _codigo; this.descricao = _desc; this.preco = _preco; this.quantidade = 0; } public void consignar() { } public void devolver() { } public double obterPreco() { return this.preco; } public Produto vender(int qtd) { Produto prodVendido; prodVendido = this.clonar(); prodVendido.quantidade = qtd; this.quantidade = this.quantidade - qtd; return prodVendido; } public Produto clonar() { Produto clone = new Produto(); clone.codigo = this.codigo; clone.preco = this.preco; clone.descricao = this.descricao; clone.quantidade = this.quantidade; return clone;
130
} public interface Venda extends Contrato{ void Vender(int codigoFun,int codigoCliente,Vector Produtos); } public class VendaProxy extends ContratoProxy implements Venda { private VendaReal vendaReal; private Persistencia bd; public VendaProxy(int codigoFun, int codigoCliente, Vector Produtos) { bd = Persistencia.obterPersistencia(); } public void Vender(int codigoFun, int codigoCliente,Vector Produtos) { } public Vector obterProdutos() { vendaReal = (VendaReal)bd.carregar(this.codigoContrato, VendaReal.class); return vendaReal.obterProdutos(); } } public class VendaReal extends ContratoReal implements Venda { public VendaReal() {} public VendaReal(int codigoFun, int codigoCliente, Vector Produtos) { this.vetorProduto = Produtos; this.calcularValorTotal(); } public void Vender(int codigoFun, int codigoCliente, Vector Produtos) { } }