Implementação de
Sistemas
Universidade Federal do Estado do Rio de Janeiro
Centro de Ciências Exatas e Tecnologia
Escola de Informática Aplicada
Curso: Bacharelado em Sistemas de Informação
Disciplina: Construção de Sistemas
Professor: Luiz Fernando Seibel
Grupo: Eduardo Henrique Cavalcante Fontanha de Oliveira
Pedro Nuno de Souza Moura
Apresentação
Tem este trabalho por objetivo funcionar como um guia e um material
especificado para os comentários referentes à apresentação sobre
Implementação de Sistemas da disciplina de Construção de Sistemas.
Em um primeiro momento, percebe-se que, após a etapa de Projeto de
Sistemas e utilizando-se a notação UML, a etapa de geração de código é
passível de automatização, dado que se têm à disposição os Diagramas de
Colaboração e o Diagrama de Classes do Projeto, isto é, o Modelo Conceitual
acrescido de métodos nas classes e sentidos nas associações entre estas.
Assim sendo, para gerar o código das classes correspondentes à
camada de domínio da aplicação, deve-se seguir a seguinte metodologia para
converter o DCP (Diagrama de Classes de Projeto) para o código da linguagem
Orientada a Objetos utilizada:
•
Classes
As classes do DCP são imediatamente convertidas em classes na
linguagem de programação.
•
Atributos
Os atributos das classes são convertidos em atributos privados, salvo casos
em que sejam discriminadamente públicos ou protegidos, da respectiva classe
na linguagem de programação. Tais atributos sempre corresponderão a
variáveis cujo tipo é built-in, ou seja, é alfanumérico. Além disso, devem ser
implementados os métodos de alteração e recuperação para esses atributos,
que são os métodos getters e setters, a fim de possibilitar o encapsulamento.
É importante notar que, a fim de viabilizar o funcionamento do mecanismo
de persistência, é fundamental acessar e modificar os atributos somente pelos
métodos getters e setters. Assim sendo, qualquer método, mesmo sendo da
própria classe, deverá acessar ou alterar tais variáveis através dos métodos
supracitados. Isso ocorre porque, quando o mecanismo de persistência for
implementado, será necessário ter controle sobre qualquer alteração feita
sobre os objetos, para verificar se estão inconsistentes com o banco de dados.
Abaixo segue um exemplo de uma classe Java com métodos para consultar
(get) e modificar (set) valores dos atributos:
class Cliente {
private String nome;
private Float debito;
private Integer idade;
public void setNome(String nome) { this.nome = nome; }
public void setDebito(Float debito) { this.debito = debito; }
public void setIdade(Integer idade) {this.idade = idade; }
public String getNome() { return nome; }
public Float getDebito() { return debito; }
public Integer getIdade() { return idade; } }
•
Associações
As associações são transformadas em variáveis de instância, da mesma
forma que os atributos, e terão métodos para alteração e consulta. Contudo, há
uma diferença: os atributos geram variáveis cujo tipo é básico (alfanumérico),
ao passo que as associações geram tipos que são classes de objetos ou
estruturas de dados. Além disso, considerando as diferentes multiplicidades de
papel e outras características das associações, haverá algumas distinções a
fazer quanto aos métodos associados.
Cada associação deverá implementar no mínimo:
a) Um método para criar a associação;
b) Um método para destruir a associação. Este método não pode ser
implementado no caso de associações para 1, porque elas não podem ser
destruídas. Associações 0..1 e * exigem a implementação de tal método; e
c) Um método para obter (consultar) os elementos associados. Este
método poderá ter muitas variantes, dependendo do tipo da associação.
Assim sendo, serão vistos e analisados os diversos tipos de associações
existentes:
•
Associação Unidirecional para 1
A associação unidirecional para 1 deve ser armazenada em uma variável de
instância na classe de origem da associação e seu tipo deve ser a classe de
destino.
Assim sendo, uma associação unidirecional de ItemDeEmprestimo para 1
Emprestimo corresponderá a uma variável de instância na classe
ItemDeEmprestimo declarada como tipo Emprestimo.
Deve-se observar que, como a associação é estritamente para 1, não é
possível destruir a associação e, portanto, esse método não deve ser
implementado. Além disso, como a associação 1 é obrigatória para o objeto
origem, o método construtor da classe deve ter como parâmetro o elemento a
ser associado, para que, desde o momento da criação, todas as instâncias da
classe na origem da associação estejam consistentes.
Figura 1: Exemplo de classe com associação unidirecional para 1
O código em Java na próxima página reflete a implementação da classe
ItemDeEmprestimo:
class itemDeEmprestimo
{
private Emprestimo emprestimo;
public ItemDeEmprestimo(Emprestimo emprestimo)
{
this.associaEmprestimo(emprestimo)
}
public void associaEmprestimo(Emprestimo emprestimo)
{
This.emprestimo = emprestimo;
}
}
•
public Emprestimo getEmprestimo()
{
return emprestimo;
}
Associação Unidirecional com Multiplicidade 0..1
Neste caso, o procedimento de geração de código é semelhante ao da
associação 0..1, mas é possível destruir a associação. Portanto, deve ser
implementado o método correspondente.
Além disso, não é necessário passar um objeto como parâmetro para o
método construtor, pois a associação 0..1 não é obrigatória
Figura 2: Exemplo de classe com associação unidirecional para 0..1
O código em Java abaixo reflete a codificação da Classe Venda:
class Venda
{
private Pagamento pagamento;
public Venda() {}
public void associaPagamento(Pagamento pagamento)
{
this.pagamento = pagamento;
}
public void desassociaPagamento()
{
This.pagamento = null;
}
}
•
public Pagamento getPagamento()
{
return pagamento;
}
Associação Unidirecional para *
A associação unidirecional para * corresponde à implementação de um
conjunto. Trata-se aqui da associação simples, isto é, não-ordenada e nãoqualificada, dado que tais relacionamentos serão abordados posteriormente.
A implementação de uma associação desse tipo constitui-se pela
implementação de uma variável do tipo Conjunto, tal como expresso na figura
3 e no respectivo código Java da classe Cliente.
Figura 3: Exemplo de classe com associação unidirecional para *
O seu código Java segue abaixo:
class Cliente {
private Set emprestimos = new HashSet();
public Cliente () { }
public void adicionaEmprestimo(Emprestimo emprestimo)
{
this.emprestimos.add(emprestimo);
}
public void removeEmprestimo(Emprestimo emprestimo)
{
this.emprestimos.remove(emprestimo);
}
}
public Set getEmprestimos ()
{
return Collections.unmodifiableSet(emprestimos);
}
Por fim, cabe citar que, caso a classe Emprestimo possuísse atributos
identificadores, então se poderia codificar métodos que retornasse um
empréstimo em particular ou um subconjunto de empréstimos.
•
Associação Unidirecional com Multiplicidade Ordenada
Quando uma associação para * for ordenada, a diferença crucial na
implementação é que se deve usar uma lista (ou um Array) como estrutura de
dados, e não um conjunto. A figura 4 demonstra um exemplo de associação
ordenada e, logo abaixo, segue o código Java classe Videolocadora.
Figura 4: Exemplo de classe com associação unidirecional ordenada para *
class Videolocadora
{
private List reservas = new ArrayList();
public Videolocadora() { }
public void adicionaReserva(Reserva reserva, int posicao)
{
this.reservas.add(posicao,reserva);
}
public void removeReserva(Reserva reserva)
{
this.reservas.remove(reserva);
}
public void removeReservaNaPosicao(int posicao)
{
this.reservas.remove(posicao);
}
public List getReservas()
{
return Collections.unmodifiableList(reservas);
}
}
public Reserva getReservaNaPosicao(int posicao)
{
return (Reserva) reservas.get(posicao);
}
Ademais, pode-se, ainda, implementer métodos de inserção como addFirst
ou addLast.
•
Associação Unidirecional com Classe de Associação
Quando a associação contém uma classe de associação, é necessário
implementar a criação e a destruição de instâncias dessa classe de associação
cada vez que uma associação é criada e removida. Classes de associação
podem existir em associações com qualquer multiplicidade. Contudo, é mais
comum serem usadas em relacionamentos do tipo * para *.
A implementação desse tipo de associação consiste em um Map, que
associa instâncias do destino da associação a instâncias da classe de
associação.
O código Java abaixo exemplifica uma classe de associação Emprego no
relacionamento * para * entre Pessoa e Empresa.
class Pessoa
{
private Map empresas = new HashMap();
public Pessoa() { }
public void adicionaEmpresa(Empresa empresa)
{
This.empresas.put(empresa, new Emprego());
}
public void removeEmpresa(Empresa empresa)
{
this.empresas.removeKey(empresa);
}
Public void removeEmprego(Emprego emprego)
{
this.empresas.removeValue(emprego);
}
public Set getEmpresa()
{
return empresas.keys();
}
public Set getEmpregos()
{
return empresas.values();
}
}
•
public Emprego getEmpregoNaEmpresa(Empresa empresa)
{
Return empresas.at(empresa);
}
Associação Unidirecional com Multiplicidade 1 na Origem
Para qualquer um dos tipos de associação unidirecional definidos, deve-se
observar se a multiplicidade na origem é 1. Se for o caso, então a destruição da
associação só será possível quando o objetivo for também destruir o objeto no
destino da associação, pois se associação for destruída, o objeto, que exige
estar associado a um elemento, fica inconsistente.
•
Associação Bidirecional
Quando a associação for bidirecional, ela deve ser implementada em
ambas as classes (com redundância controlada). Independente de a
multiplicidade ser para 1, para 0..1 ou para *, cada lado da associação será
implementada de acordo com as regras expostas anteriormente. Abaixo segue
o exemplo de uma associação de 1 para *.
Figura 5: Exemplo de associação bidirecional de 1 para *
O código Java das classes Cliente e Emprestimo seguem abaixo:
class Cliente
{
private Set emprestimos = new HashSet();
public Cliente () {}
public void adicionaEmprestimoAux(Emprestimo emprestimo)
{
emprestimos.add(emprestimo);
}
public void removeEmprestimoAux(Emprestimo emprestimo)
{
emprestimos.remove(emprestimo);
}
public void adicionaEmprestimo(Emprestimo emprestimo)
{
if (emprestimo.getCliente() != null) {
emprestimo.getCliente().removeEmprestimoAux(emprestimo);
}
}
this.adicionaEmprestimoAux(emprestimo);
emprestimo.associaClienteAux(this);
public void removeEmprestimo(Emprestimo emprestimo)
{
this.removeEmprestimoAux(emprestimo);
emprestimo.destroi();
}
}
public Set getEmprestimos() {
return emprestimos;
}
class Emprestimo
{
private Cliente cliente;
public Emprestimo(Cliente cliente)
{
this.associaCliente(cliente);
}
public void associaClienteAux(Cliente cliente)
{
this.cliente = cliente;
}
public void associaCliente(Cliente cliente)
{
if(this.cliente != null)
{
this.cliente.removeEmprestimoAux(this);
}
}
}
this.associaClienteAux(cliente);
cliente.adicionaEmprestimoAux(this);
public Cliente getCliente()
{
return cliente;
}
Métodos Delegados
Métodos delegados e operações de sistema são gerados a partir da
observação dos Diagramas de Colaboração. Assim sendo, um método deve
ser implementado em uma dada classe sempre que em algum Diagrama de
Colaboração uma instância de tal classe receber uma mensagem delegada.
Abaixo segue um exemplo:
Figura 6: Diagrama de Colaboração
A classe Cliente recebe a mensagem delegada emprestaFita(f). Abaixo
segue o código Java da Classe Cliente para tal diagrama de colaboração:
class Cliente {
private Emprestimo emprestimoAberto;
}
public void emprestaFita(Fita f)
{
if (this.getEmprestimoAberto() == null)
{
this.criaNovoEmprestimo();
}
this.getEmprestimoAberto().criaNovoItem(f);
}
Padrões de Implementação
O código a ser gerado durante a fase de Implementação do Sistema
necessita ser de fácil compreensão, a fim de qualquer outro desenvolvedor
possa entendê-lo, e de fácil manutenibilidade, a fim de que se possa realizar
a sua manutenção no futuro.
Dessa forma, os padrões de implementação definem os critérios de
qualidade esperados para o código. Definem, pois, regras e recomendações
em geral a serem utilizadas durante o desenvolvimento. Têm como objetivo
ajudar os desenvolvedores a produzir artefatos de qualidade e oferecem
instrumentos para se avaliar a qualidade.
Um possível conteúdo para padrões de implementação é:
•
Regras para composição de módulos
o Nomes e extensões de arquivos
•
Padrão para especificações (documentação)
o Comentários
//
/* */
o Cabeçalhos de identificação de arquivos
o /* Nome: classe.cpp */
o /* Projeto: TDPV */
o /* Versão: */
o /* Autores: */
o /* Descrição: informações gerenciais */
o Histórico de alterações
O benefício do padrão só será percebido caso seja adotado por todos
os participantes. Algumas regras podem ser asseguradas por intermédio das
ferramentas de codificação. Por fim, se faz imperioso:
• Apresentar o padrão aos interessados;
• Treinar os participantes com o uso do padrão; e
• Controlar sua adoção.
Por fim, cabe explicitar que indubitavelmente, sem exageros, os padrões
são fundamentais para eliminar os personalismos que possam danificar a
qualidade dos programas.
MVC (Model-View-Controller)
A motivação preponderante do modelo MVC é possibilitar que uma
mesma aplicação atenda a diferentes tipos de usuários, cada um com a sua
própria interface.
Figura 7: Ilustração de uma aplicação com diferentes usuários
Assim sendo, é uma arquitetura de software que separa a lógica de
controle (Controller), a interface do usuário (View) e a lógica da aplicação
(Model) em três componentes distintos, de forma que modificações em um
componente possa ser realizada com o mínimo de impacto nos demais
componentes. Mais especificamente, possui como pilar sustentador a
separação da lógica do domínio da aplicação da interface com o usuário, visto
que alterações em tais camadas são direcionadas por interesses diferentes e
em circunstâncias diferentes.
As três camadas componentes de tal paradigma seguem abaixo:
•
Modelo
O modelo contém o núcleo funcional da aplicação. Dessa forma, representa
os dados e as regras de negócio que controlam acessos e atualizações a
esses dados. Ademais, não possui qualquer informação sobre a interface com
o usuário.
•
Vista
Corresponde às classes que representam os elementos na interface com o
usuário. Todos os objetos que o usuário pode ver e responder diretamente na
tela, tais como: botões, caixas de texto, etc.
É esta camada que recebe a entrada de dados e apresenta o resultado.
Assim sendo, acessa o dado através do modelo e especifica como aquele dado
deve ser apresentado. É, pois, responsável por manter a consistência em sua
apresentação quando o modelo muda.
•
Controle
O controle é responsável por intermediar, isto é, traduzir interações com a
Vista em ações a serem executadas pelo Modelo. Portanto, torna-se de fácil
percepção que seu cerne é selecionar a vista apropriada, isto é, dizer como
esta deve se comportar, baseado nas interações do usuário e no resultado das
ações do modelo.
Há dois tipos de MVC: MVC1 e MVC2. As figuras abaixo ilustram ambos:
Figura 8: Ilustração do modelo MVC1 (MVC Model1 ou JSP Model 1)
Figura 9: Ilustração do modelo MVC2 (MVC Model 2 ou JSP Model 2)
Figura 10: Comparação do modelo MVC1 e o MVC2
Mostra-se, pois, de fácil percepção que a diferença é que, no MVC2,
voltado para a web, o modelo não faz atualizações na vista.
Alguns benefícios do uso de MVC são:
- Reuso de componentes do Modelo;
- Facilita o suporte de novos tipos de cliente;
- Várias vistas simultâneas do mesmo modelo;
- Vistas Sincronizadas;
- Facilita manutenção da interface;
- Facilita testes;
- Separação de papéis de desenvolvedores;
- Modularidade eficiente; e
- Facilita a expansão.
Design Pattern
Na engenharia de software, um design pattern é uma solução geral para
problemas que ocorrem com certa freqüência. Baseia-se em descrições, ou
templates, ou uma série de definições de regras e passos a serem
desenvolvidos durante a confecção de um código que pode ser usado em
inúmeras diferentes situações.
Os Patterns foram criados como um conceito de arquitetura por
Christopher Alexander. Em 1987 começaram a fazer experimentos com a idéia
de aplicar patterns à programação, e uma apresentação a este respeito foi feita
na conferência de OOPSLA daquele ano. Após a publicação do livro Design
Patterns: Elements of Reusable Object-Oriented Software, os design patterns
ganharam uma grande popularidade.
Exemplos de Web Patterns – Abordagem com Struts
Em aplicações Web podemos seguir diferentes processos de
implementação de código. Ao implementarmos, de fato, percebemos que os
design patterns podem ser divididos em algumas tantas classificações.
Desenvolvimento Web com Struts aborda a implementação em camadas
conhecida como MVC2, que fora mostrada anteriormente. Mais
especificamente, neste caso, suas camadas são:
1 – Modelo: será composta de classes Java-Beans (isto é, classes com
propriedades e “getters and setters”) e responsável por persistência de objetos.
2 – Visão: será composta de todo código relacionado à exibição de dados na
tela para o usuário (JSP’s, formulários);
3 – Controle (Servlet): camada de servlet implementada pelo próprio Struts,
que fará a comunicação, através de um XML, entre a visão e o controle; e
4 – Controle (Actions): camada responsável por execução de ações no
sistema e validação de dados.
Um pattern estrutural que há na criação de modelos, é quando se tratam
exceções com instâncias de sessões entrada e saída de dados abertas. Há um
problema quando se fala em persistência, pois para realização de transações e
bom uso do banco, sempre que uma transação é terminada, com erro ou não,
a sessão que a realizou (a conexão com o banco de dados) tem que ser
fechada.
Podemos ter supostamente uma classe Conexao que cria essa sessão
com o banco de dados, e o código abaixo:
public Object método(){
Object o = null;
Conexao conexão = new Conexão(); //abre uma conexão
try{
//operações
} catch (Exception e){
}
//instruções
} finally {
conexao.close();
}
return o;
Este código é uma definição, um design pattern estrutural, de como se
deve proceder para a manutenção de operações com um banco de dados,
resolvendo a questão da obrigatoriedade de se fechar toda sessão que se
inicia.
Em aplicações que utilizam o framework Struts, temos 4 formas de
realizarmos as validações de entrada no sistema. São elas:
- Validação por Action;
- Validação por Form através do método validate();
- Validação direta por cada input presente no Form; e
- Validação automática.
Nas validações pela Action, basta que controlemos os valores que são
agregados a cada propriedade das classes Form-Bean, ou dos Forms
dinâmicos da aplicação.
Um design pattern para esse tipo de situação poderia ser descrito da
seguinte forma:
public class ExemploAction extends Action{
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response){
ExemploForm exemploForm = (ExemploForm) form;
if(exemploForm.getExemplo().trim().equals(“”){
//Tratamento para o erro.
}
//Operações da ação de exemplo.
}
}
return mapping.findForward(“paginaExemplo”);
Com isso, o programador sabe que, ao desenvolver uma Action, primeiro
ele vai tratar as propriedades do Form-Bean, e que só então, depois de
trabalhados os erros de input, ele realizará as operações da dada Action.
A validação automática é a mais especial das validações, pois é capaz
de gerar validações tanto na aplicação do lado do servidor quanto do lado do
cliente (através de funções em JavaScript). Contudo, essas validações são
limitadas (simples) e através de arquivos XML.
As validações diretas nas propriedades do Form-Bean, e no método
validate() (da classe pai ActionForm) são úteis, pois impedem a execução da
Action em caso de erro. Porém, elas não se aplicam a regras de negócio mais
complexas, em que os testes de validação vão além dos campos de input em
uma página.
Bibliografia
1. WAZLAWICK, Raul Sidnei. Análise e Projeto de Sistemas de Informação
Orientados a Objetos. Rio de Janeiro: Elsevier, 2004.
2. Material didático da professora Renata Araújo, referente à disciplina de
Análise e Projeto de Sistemas, no período de 2006/1 do curso de Sistemas de
Informação da UNIRIO. Disponível em: www.uniriotec.br/~renata.araujo/APS.
3. Material didático do professor Leonardo Azevedo, referente à disciplina de
Projeto e Construção de Sistemas com Ambiente de Programação, no período
de 2006/2 do curso de Sistemas de Informação da UNIRIO. Disponível em:
http://leogazevedo.googlepages.com/pcsap.
4. Material didático do professor Márcio Barros, referente à disciplina de
Desenvolvimento de Software para Web, no período de 2007/1 do curso de
Sistemas de Informação da UNIRIO.
5. Notas de aula da disciplina de Análise e Projeto de Sistemas, do curso de
Sistemas de Informação da UNIRIO, ministrada pela professora Renata Araújo
no período de 2006/1.
6. Notas de aula da disciplina de Projeto e Construção de Sistemas com
Ambiente de Programação, do curso de Sistemas de Informação da UNIRIO,
ministrada pelo professor Leonardo Azevedo no período de 2006/2.
7. Notas de aula da disciplina de Desenvolvimento de Software para Web, do
curso de Sistemas de Informação da UNIRIO, ministrada pelo professor Márcio
Barros no período de 2007/1.
Download

Implementação de Sistemas - Centro de Ciências Exatas e Tecnologia