Programação Orientada a Objetos no C# .NET usando Padrões de

Propaganda
POO
Programação Orientada a
Objetos no C# .NET usando
Padrões de Projeto
MARCELO SANTOS DAIBERT E MARCO ANTÔNIO PEREIRA ARAÚJO
Marcelo Santos Daibert ([email protected]) é professor do Curso de Bacharelado em Ciência
da Computação da FAGOC - Faculdade Governador Ozanam Coelho na graduação e pósgraduação (especialização), Mestrando e Especialista em Ciência da Computação pela
Universidade Federal de Viçosa e Bacharel em Sistemas de Informação pela Faculdade Metodista
Granbery. Gerente técnico da Optical Soluções em Informática.
Marco Antônio Pereira Araújo ([email protected]) é professor do Curso de Bacharelado
em Sistemas de Informação da Faculdade Metodista Granbery, Doutorando e Mestre em
Engenharia de Sistemas e Computação pela COPPE/UFRJ, Especialista em Métodos Estatísticos
Computacionais e Bacharel em Informática pela UFJF, Analista de Sistemas da Prefeitura de Juiz
de Fora.
Este artigo discute
• Orientação a Objetos;
• Padrões de Projeto;
• Mapeamento Objeto-Relacional;
• Criação de classes;
• Programação em camadas;
• Persistência de objetos.
Este artigo usa as seguintes tecnologias
Visual Studio 2008, C#, SQL Server 2005.
Com o passar dos anos foi possível observar uma profunda evolução na utilização de
computadores. Hoje graças a esta evolução, juntamente com fenômenos como a globalização, é
possível estar em sincronia com as notícias de todas as partes do mundo. Nunca se utilizou tanto
quanto atualmente recursos computacionais, firmando assim esta tecnologia como necessária para a
vida moderna atual. Neste sentido, várias evoluções foram necessárias para se alcançar o atual
patamar e novas outras evoluções se fazem necessárias para alcançar novos patamares que se
reservam no futuro.
Na área da engenharia de software não foi diferente. O desenvolvimento de software se
amadureceu e ainda deve amadurecer com o passar dos anos. Para autores da área, fatores chaves
para que houvesse mudanças e evolução no desenvolvimento de software nos últimos anos são:
mudanças na economia, interface com o usuário, operações em rede, tempo de disponibilização para
o mercado (time to market - TTM), problemas com o modelo em cascata, tecnologia de objetos (OO
– Orientação a Objetos) e computação em desktops.
A qualidade de software passou a ser um tema muito discutido. O foco para a qualidade dos
sistemas desenvolvidos dentro de uma organização ganhou papel principal entre as discussões dos
engenheiros. Assim, várias são as abordagens de desenvolvimento que buscam garantir esta
qualidade.
Neste contexto, o C# é uma linguagem de programação que une o poder e a flexibilidade da
linguagem C/C++ com a produtividade de linguagens e ambientes de desenvolvimento rápido
(RAD - Rapid Application Development). Quando aplicadas técnicas da programação orientada a
objetos, com padrões de desenvolvimento de software, a linguagem se mostra uma poderosa
ferramenta para desenvolvimento de software, com suporte as mais novas tecnologias do mercado.
Este artigo tem o objetivo de abordar de forma prática o desenvolvimento de uma aplicação,
usando a linguagem C# do Visual Studio .NET 2008, utilizando os recursos da Programação
Orientada a Objetos (POO), do .NET, contextualizando a utilização de alguns padrões de projeto,
em especial o padrão DAO (Data Access Object) para persistência e manipulação dos objetos da
aplicação. Para armazenar os dados da aplicação desenvolvida é utilizado o banco de dados MSSQL Server 2005. Toda a aplicação é desenvolvida com o conceito de programação em camadas,
também discutido no artigo.
Um dos desafios no desenvolvimento de aplicações orientadas a objetos é a persistência de
objetos. Os bancos de dados puramente orientados a objetos são de fato os mais adequados para a
persistência, mas a indisponibilidade atual desses, seja devido ao custo, diversidade e
principalmente amadurecimento, faz com que seja necessária uma busca por alternativas para a
realização dessa tarefa.
Uma das soluções encontradas para o problema é a utilização de camadas de persistência para
a manipulação dos objetos utilizando bancos de dados relacionais. Essa abordagem vem sendo
amplamente utilizada no mercado, principalmente para o desenvolvimento de sistemas de médio a
grande porte.
Basicamente une a rapidez e o amadurecimento das bases de dados relacionais com os
benefícios da programação orientada a objetos (POO). A função principal de uma camada de
persistência é portal transparentemente os objetos de uma aplicação para a base de dados relacional
de forma genérica.
Para isso, são utilizas principalmente técnicas de mapeamento objeto-relacional com o intuito
de mapear as classes e associações para tabelas e relacionamentos. A utilização de uma camada de
persistência, embora pareça trivial inicialmente, se mostra complexa na prática, uma vez que são
várias as configurações que devem ser feitas para que a camada funcione corretamente, além de
outros fatores que dificultam sua utilização. A edição 34 da revista .NET Magazine, antes chamada
de MSDN Magazine, foi dedicada a esta abordagem, onde o estudo de caso apresentou a utilização
de um framework de persistência chamado nHibernate.
Utilizando esse mesmo raciocínio, outra solução, a abordada neste artigo, apresenta o mesmo
estudo de caso apresentado na edição 34, mas com uma diferença: A própria aplicação faz o
mapeamento objeto-relacional e a conexão com o banco de dados. Para isso, é apresentado o padrão
de projeto DAO, que se apresenta como outra camada do sistema, com a responsabilidade de
conectar no banco de dados, persistir e manipular os objetos da aplicação.
Padrões de Projeto (Design Patterns): são soluções elegantes e reutilizáveis para problemas
recorrentes do processo de desenvolvimento de software Orientado a Objetos.
Estudo de Caso
Como já apresentado, o estudo de caso deste artigo é o mesmo apresentado na edição 34 desta
revista, mas com a diferença de utilizar outra abordagem na persistência de objetos. O estudo de
caso apresenta um fragmento de um sistema maior, representado aqui pela relação entre
funcionários e departamentos de uma empresa, através das classes Departamento, Funcionario e
FuncionarioHorista. A classe Funcionario é abstrata, servindo para a definição dos elementos
comuns às suas subclasses através da herança.
Todos os funcionários devem estar lotados em um Departamento, que pode ter vários
funcionários. A Figura 1 representa o diagrama de classes do nosso sistema.
Funcionario
codigo : String
nome : String
cpf : String
0..*
1
calcularSalario : float()
Departamento
codigo : String
Descricao : String
FuncionarioHorista
numDiasTrabalhados : int
numHorasDiaTrabalhado : int
valorHora : int
Figura 1. Diagrama de Classes do estudo de caso.
Através desse estudo de caso, são abordados temas como classes, objetos, métodos, atributos,
persistência de objetos, herança, polimorfismo, herança, diálogos, associações e programação em
camadas MVC (Model-View-Controller), além de temas pertinentes ao ambiente de
desenvolvimento, como utilização de componentes, classes e controles disponibilizados pelo Visual
Studio 2008 e o framework .NET.
O estudo de caso propõe o desenvolvimento em camadas, utilizando o padrão de projeto
estrutura MVC, composto por três camadas fundamentais. Esta abordagem busca, entre outras
coisas, o desacoplamento de funções da aplicação, facilitando assim o desenvolvimento e
garantindo maior manutenibilidade da aplicação. Além das três camadas do MVC, é ainda
apresentada uma outra camada, a responsável pela persistência dos dados. Esta camada, aqui
chamada de camada de persistência tem a responsabilidade de comunicar com o banco de dados e
de persistir e manipular os objetos neste banco. A Figura 2, representa as camadas do estudo de
caso e como é feita a comunicação entre elas.
Classes de Interface com usuário (View) Modelo
MVC
Classes de Controle (Controller) Classes de Negócio (Model) Camada de Persistência (Classes DAO)
Banco de Dados
SQL
Figura 2. Camadas do Estudo de caso.
No contexto do estudo de caso, o View é representado pelas classes de interface com o
usuário. Estas se comunicam com os controladores de das classes de domínio. As classes
controladoras (Controller) têm a finalidade de ser um elo entre o Model e o View, ou seja, entre as
classes de domínio da aplicação e a interface, garantindo assim o desacoplamento. Já o Model é
representado pelas classes de domínio, e que neste estudo de caso são as classes Funcionario,
FuncionarioHorista e Departamento. O Model faz acesso às classes de persistência DAO para
persistir e manipular os seus objetos. E esta, por sua vez, faz acesso ao banco de dados e
efetivamente faz a tarefa de persistir e manipular os objetos.
Classes do Modelo, Propriedades e Métodos
Para iniciar o desenvolvimento do estudo de caso, foi criado um projeto no Visual Studio
chamado Empresa. Este projeto será divido em cinco namespaces, como pode ser visualizado na
Figura 3. O namespace View, para armazenar as classes de interface, o Controller, para as classes
controladoras, o Model, para as classes de domínio, o Persistence para as classes DAO de acesso ao
banco de dados e por fim, o namespace Resources, para armazenar as imagens utilizadas nas
interfaces.
Figura 3. Namespaces do projeto do estudo de caso.
As listagens 1, 2 e 3 apresentam o código-fonte de definição das classes Funcionario,
FuncionarioHorista e Departamento, respectivamente, do estudo de caso proposto, todas dispostas
no namespace Model e com os nomes: Funcionario.cs, FuncionarioHorista.cs e Departamento.cs.
Listagem 1. Definição da Classe Funcionario
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
using
using
using
using
using
System;
System.Collections.Generic;
System.Collections;
System.Text;
Empresa.Persistence;
namespace Empresa.Model{
public abstract class Funcionario{
public Funcionario(String pNome, String pCPF){
this.Nome = pNome;
this.CPF = pCPF;
this.Departamento = new Departamento();
}
public Funcionario(){
}
private String codigo;
18.
private String nome;
19.
private String cpf;
20.
private Departamento departamento;
21.
22.
public String Codigo{
23.
get { return this.codigo; }
24.
set { this.codigo = value; }
25.
}
26.
public String Nome{
27.
get { return this.nome; }
28.
set { this.nome = value; }
29.
}
30.
public String CPF{
31.
get { return this.cpf; }
32.
set { this.cpf = value; }
33.
}
34.
public Departamento Departamento{
35.
get { return this.departamento; }
36.
set { this.departamento = value; }
37.
}
38.
public abstract float calcularSalario();
39.
public abstract Boolean Persistir();
40.
public abstract Boolean Atualizar();
41.
public static Funcionario RecuperaObjeto(String pID){
42.
Funcionario objFuncionario = FuncionarioDAO.recuperaObjeto(pID);
43.
return objFuncionario;
44.
}
45.
public static IList RecuperaObjetos(){
46.
IList listFuncionarios = FuncionarioDAO.recuperaObjetos();
47.
return listFuncionarios;
48.
}
49.
public static IList RecuperaObjetosPorDepartamento(String pOID){
50.
IList listFuncionarios =
FuncionarioDAO.recuperaObjetosPorDepartamento(pOID);
51.
return listFuncionarios;
52.
}
53.
public static Boolean Excluir(String pID){
54.
return FuncionarioDAO.excluir(pID);
55.
}
56.
}
57. }
Na Listagem 1, a linha 8 exibe a assinatura da classe abstrata Funcionario. Ela é abstrata por
não instanciar objetos, tendo por objetivo definir os elementos comuns às suas subclasses. Entre as
linhas 17 e 20 são apresentados os atributos codigo, nome, cpf e departamento da classe. Todos com
visibilidade privada, sendo acessados pelas propriedades definidas entre as linhas 22 e 37. As
propriedades, nesse caso, agem como métodos gets e sets, mantendo, portanto, o encapsulamento da
classe.
Nota: Encapsulamento é a prática de limitar o acesso às informações de um objeto, fazendo
com que somente os seus métodos tenham acesso aos dados e à manipulação dos atributos. O
encapsulamento disponibiliza o objeto com toda sua funcionalidade sem a necessidade de saber
internamente o funcionamento, aumentando assim a manutenibilidade do sistema.
Das linhas 9 a 15 são apresentados os métodos construtores da classe, onde temos dois: um
com passagem de parâmetros dos atributos na sua construção e outro sobrecarregando o anterior
sem parâmetros, usado para instanciar um objeto sem informações para seus atributos. O método
sobrecarregado (overload) é definido quando a assinatura do mesmo, difere do anterior na passagem
de parâmetros. Uma classe pode ter quantos métodos sobrecarregados forem necessários.
Na linha 38 é exibido a assinatura do método abstrato calcularSalario. Ele é abstrato, pois é
redefinido nas subclasses, de acordo com a necessidade específica de implementação. A isso é dado
o nome de polimorfismo, que é a capacidade de um método de mesma assinatura comportar-se de
maneira diferente, em uma mesma hierarquia.
Exemplificando o uso, o método calcularSalario possui a mesma assinatura em toda a
hierarquia, no entanto, ele se comporta de maneira diferente na subclasse FuncionaoHorista, uma
vez que o algoritmo para calcular salário é diferente nessa subclasse. O fato do método ser definido
como abstrato (abstract) em sua assinatura, obriga suas subclasses a implementá-lo.
Entre as linhas 39 e 55 são definidos os métodos responsáveis pela persistência e manipulação
dos objetos do tipo Funcionario. Observe que todos esses métodos fazem acesso às classes DAO,
que é importado para a classe pela linha 5 (using Empresa.Persistence). Como já apresentado, as
classes DAO são as responsáveis pela conexão com o banco de dados e pela persistência e
manipulação dos objetos. De acordo com a programação em camadas, estes métodos serão
chamados pelos controladores da classe para finalmente, a classe fazer as chamadas para as classes
DAO como apresentado acima.
Observe que são definidos algum métodos com o modificador static, como por exemplo, na
linha 45. Este indica que o método é um método de classe e não é necessário instanciar objeto para
que o mesmo seja invocado.
Nota: Método de Classe é um método que não há a necessidade se instanciar um objeto da
classe para poder acessá-lo. Diferentemente do método de instância, que como o próprio nome diz,
é necessário a instanciação do objeto para sua invocação.
Listagem 2. Definição da Classe FuncionarioHorista
1. using System;
2. using System.Collections.Generic;
3. using System.Collections;
4. using System.Text;
5. using Empresa.Persistence;
6.
7. namespace Empresa.Model{
8.
class FuncionarioHorista : Funcionario{
9.
public FuncionarioHorista(String pNome, String pCPF, int pValorHora, int
pNumDiasTrabalhado, int pNumHorasTrabalhada)
10.
: base(pNome, pCPF){
11.
this.valorHora = pValorHora;
12.
this.numHorasDiaTrabalhado = pNumHorasTrabalhada;
13.
this.numDiasTrabalhados = pNumDiasTrabalhado;
14.
}
15.
public FuncionarioHorista(){
16.
}
17.
18.
private int numDiasTrabalhados;
19.
private int numHorasDiaTrabalhado;
20.
private int valorHora;
21.
22.
public int NumDiasTrabalhados{
23.
get { return this.numDiasTrabalhados; }
24.
set { this.numDiasTrabalhados = value; }
25.
}
26.
public int NumHorasDiaTrabalhado{
27.
get { return this.numHorasDiaTrabalhado; }
28.
set { this.numHorasDiaTrabalhado = value; }
29.
}
30.
public int ValorHora{
31.
get { return this.valorHora; }
32.
set { this.valorHora = value; }
33.
}
34.
35.
public override float calcularSalario(){
36.
return this.valorHora * this.numHorasDiaTrabalhado *
this.numDiasTrabalhados;
37.
}
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58. }
public override Boolean Persistir(){
FuncionarioHoristaDAO objFunHoristaDAO = new FuncionarioHoristaDAO();
if (objFunHoristaDAO.persistir(this)){
return true;
}else{
return false;
}
}
public override Boolean Atualizar(){
FuncionarioHoristaDAO objFunHoristaDAO = new FuncionarioHoristaDAO();
if (objFunHoristaDAO.atualizar(this))
{
return true;
}
else
{
return false;
}
}
}
Já na Listagem 2, é definida a classe FuncionarioHorista, com seus atributos, propriedades e
métodos. Importante observar nas linhas 8 e 16, no método construtor da classe, a utilização da
palavra reservada base. Essa é utilizada para invocar o método construtor do pai, passando os
parâmetros referentes à super classe.
Deve sempre suceder a assinatura do método separado por dois pontos. Lembrando que a
ordem dos parâmetros deve ser a mesma da assinatura, assim como em qualquer chamada de
método. A linha 35 apresenta a definição do método calcularSalario. Como esse método remete ao
método abstrato do pai, o mesmo deve ser definido como override. Esse sim com implementação
em sua definição.
Listagem 3. Definição da Classe Departamento
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Collections;
System.Text;
System.Windows.Forms;
Empresa.Persistence;
namespace Empresa.Model{
public class Departamento{
public Departamento(){
}
private String codigo;
private String descricao;
public String Codigo{
get { return this.codigo; }
set { this.codigo = value; }
}
public String Descricao{
get { return this.descricao; }
set { this.descricao = value; }
}
public Boolean Persistir(){
DepartamentoDAO objDepartamentoDAO = new DepartamentoDAO();
if (objDepartamentoDAO.persistir(this)){
return true;
}else{
return false;
}
}
public Boolean Atualizar(){
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53. }
DepartamentoDAO objDepartamentoDAO = new DepartamentoDAO();
if (objDepartamentoDAO.atualizar(this)){
return true;
}
else{
return false;
}
}
public static Departamento RecuperaObjeto(String pID){
Departamento objDepartamento = DepartamentoDAO.recuperaObjeto(pID);
return objDepartamento;
}
public static IList RecuperaObjetos(){
IList listDepartamentos = DepartamentoDAO.recuperaObjetos();
return listDepartamentos;
}
public static Boolean Excluir(String pID){
return DepartamentoDAO.excluir(pID);
}
}
Por fim, na Listagem 3 é apresentada a classe Departamento, com seus atributos,
propriedades e métodos. Na linha 10 é exibido o método construtor do objeto. Entre as linhas 13 e
23 são exibidos os atributos e propriedades da classe.
Nas classes apresentadas nas listagens anteriores são definidos os métodos Persistir (persiste
um objeto), Atualizar (atualiza um objeto já persistido), RecuperaObjeto (recupera um único objeto
especificado), RecuperaObjetos (recupera todos os objetos da classe referenciada) e Excluir (exclui
um objeto especificado). Esses são os métodos responsáveis pela persistência e manipulação dos
objetos no sistema. Observe que todos fazem acesso às classes DAO, do namespace Persistence. A
classe Funcionario possui ainda o método RecuperaObjetosPorDepartamento. Este método
recupera todos os funcionários de um determinado departamento especificado.
Classes de Interface e Controladores
Dentro do namespace View estão dispostas as classes com as interfaces, como visualizado na
Figura 4. São definidas as seguintes interfaces: frmPrincipal (a interface principal do sistema,
apresentada na Figura 5), frmSplashScreen (com a interface de inicialização da aplicação), frmSobre
(interface sobre do sistema), frmListarTemplate (define um template para reutilização de interfaces,
baseando-se no conceito de herança de interfaces), frmListarFuncionario (herda de
frmListarTemplate e apresenta a interface de visualização de todos os funcionários),
frmListarDepartamento (também herda de frmListarTemplate e apresenta a interface de
visualização de todos os departamentos), frmNovoFuncionarioTemplate (define o template para a
interface de novos funcionários), frmNovoFuncionarioHorista (interface para criar um novo
funcionário horista), frmNovoDepartamento (interface para criar um novo departamento no
sistema), e por fim a interface frmFuncionariosDepartamento (lista todos os funcionários de um
determinado departamento).
Figura 4. Classes de Interface.
Figura 5. Interface Principal do Sistema - frmPrincipal.
As classes de interface definem as interfaces da aplicação (View). Estas, por sua vez, fazem
acesso às classes controladoras (Controller), que então fazem acesso às classes do modelo (Model),
já definidas. E as classes do modelo fazem acesso às classes de persistência DAO (Persistence
DAO).
As classes controladoras são definidas dentro do namespace Controller. As Listagens 4 e 5
apresentam respectivamente os controladores cntrFuncionario e cntrFuncionarioHorista. Todos
possuem a mesma funcionalidade: elo de ligação entre o model e o view. A estrutura também é
muito semelhante em todas essas classes. Elas basicamente possuem um atributo do tipo da classe
que gerenciam, menos para a cntrFuncionario, já que esta gerencia uma classe abstrata. Os métodos
também possuem a mesma estrutura e todos fazem acesso ao modelo. Basicamente existe o método
Salvar, responsável por persistir ou atualizar um objeto, o método RecuperaObjeto, para recuperar
um determinado objeto, o RecuperaObjetos, para recuperar todos os objetos da classe persistidos e
o método Excluir, responsável por excluir um determinado objeto.
Listagem 4. Definição da Classe cntrFuncionario
1.
2.
3.
4.
5.
6.
7.
8.
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Collections;
System.Text;
Empresa.Model;
Empresa.Persistence;
namespace Empresa.Controller{
9.
class cntrFuncionario{
10.
public cntrFuncionario(){
11.
}
12.
public static ArrayList RecuperaObjetos(){
13.
ArrayList vetEnviaFuncionarios = new ArrayList();
14.
IList listFuncionarios = Funcionario.RecuperaObjetos();
15.
for (int i = 0; i < listFuncionarios.Count; i++)
16.
{
17.
Funcionario objBuffer = (Funcionario)listFuncionarios[i];
18.
String[] vBuffer = new String[4];
19.
vBuffer[0] = objBuffer.Codigo;
20.
vBuffer[1] = objBuffer.Nome;
21.
vBuffer[3] = objBuffer.Departamento.Descricao;
22.
if (objBuffer is FuncionarioHorista)
23.
{
24.
vBuffer[2] = "Horista";
25.
}
26.
vetEnviaFuncionarios.Add(vBuffer);
27.
}
28.
return vetEnviaFuncionarios;
29.
}
30.
public static ArrayList RecuperaObjetosPorDepartamento(String pOID){
31.
ArrayList vetEnviaFuncionarios = new ArrayList();
32.
IList listFuncionarios =
Funcionario.RecuperaObjetosPorDepartamento(pOID);
33.
for (int i = 0; i < listFuncionarios.Count; i++)
34.
{
35.
Funcionario objBuffer = (Funcionario)listFuncionarios[i];
36.
String[] vBuffer = new String[3];
37.
vBuffer[0] = objBuffer.Codigo;
38.
vBuffer[1] = objBuffer.Nome;
39.
vBuffer[2] = Convert.ToString(objBuffer.calcularSalario());
40.
vetEnviaFuncionarios.Add(vBuffer);
41.
}
42.
return vetEnviaFuncionarios;
43.
}
44.
public static Boolean Excluir(String pID){
45.
return Funcionario.Excluir(pID);
46.
}
47.
public static int ContabilizaFuncionariosPorDepartamento(String
pIDDepartamento){
48.
return
FuncionarioDAO.ContabilizaFuncionariosPorDepartamento(pIDDepartamento);
49.
}
50.
}
51. }
Na Listagem 4, a classe cntrFuncionario apresenta três métodos estáticos, responsáveis pela
manipulação genérica de objetos de toda a hierarquia da classe Funcionario. O primeiro é o
RecuperaObjetos responsável por retornar à interface, um vetor aninhado com todos os objetos do
tipo Funcionario da base de dados.
Para isso, é invocado o RecuperaObjetos da classe Funcionario, que retorna uma coleção de
objetos. Com essa coleção, é então montado o vetor com os dados de cada funcionário, que será
retornado à interface. O método RecuperaObjetosPorDepartamento faz ação semelhante ao
RecuperaObjetos, mas filtrando os funcionários por um determinado departamento. Para isso, o
método recebe por parâmetro o OID (Object Identification – Atributo identificador) do
departamento. Desta forma, o método invoca então o método da classe Funcionario
RecuperaObjetosPorDepartamento. Já o método Excluir se responsabiliza em receber como
parâmetro uma string representando o OID do objeto que a interface deseja excluir e aciona o
Excluir da classe Funcionario.
Listagem 5. Definição da Classe cntrFuncionarioHorista
1.
2.
3.
using System;
using System.Collections.Generic;
using System.Collections;
4. using System.Text;
5. using Empresa.Model;
6.
7. namespace Empresa.Controller{
8.
class cntrFuncionarioHorista{
9.
private FuncionarioHorista objFuncionarioHorista;
10.
public cntrFuncionarioHorista(){
11.
}
12.
public Boolean Salvar(ArrayList pLista){
13.
if (pLista[0] == null){
14.
//Criar um Novo
15.
this.objFuncionarioHorista = new FuncionarioHorista();
16.
this.objFuncionarioHorista.Codigo = System.Guid.NewGuid().ToString();
17.
this.objFuncionarioHorista.Nome = Convert.ToString(pLista[1]);
18.
this.objFuncionarioHorista.CPF = Convert.ToString(pLista[2]);
19.
this.objFuncionarioHorista.Departamento =
Departamento.RecuperaObjeto(Convert.ToString(pLista[3]));
20.
this.objFuncionarioHorista.NumDiasTrabalhados =
Convert.ToInt16(pLista[4]);
21.
this.objFuncionarioHorista.NumHorasDiaTrabalhado =
Convert.ToInt16(pLista[5]);
22.
this.objFuncionarioHorista.ValorHora = Convert.ToInt16(pLista[6]);
23.
if (objFuncionarioHorista.Persistir()){
24.
return true;
25.
}
26.
else{
27.
return false;
28.
}
29.
}
30.
else{
31.
//Atualização
32.
this.objFuncionarioHorista =
(FuncionarioHorista)Funcionario.RecuperaObjeto(pLista[0].ToString());
33.
this.objFuncionarioHorista.Nome = Convert.ToString(pLista[1]);
34.
this.objFuncionarioHorista.CPF = Convert.ToString(pLista[2]);
35.
this.objFuncionarioHorista.Departamento =
Departamento.RecuperaObjeto(Convert.ToString(pLista[3]));
36.
this.objFuncionarioHorista.NumDiasTrabalhados =
Convert.ToInt32(pLista[4]);
37.
this.objFuncionarioHorista.NumHorasDiaTrabalhado =
Convert.ToInt32(pLista[5]);
38.
this.objFuncionarioHorista.ValorHora = Convert.ToInt32(pLista[6]);
39.
if (objFuncionarioHorista.Atualizar()){
40.
return true;
41.
}
42.
else{
43.
return false;
44.
}
45.
}
46.
}
47.
public ArrayList RecuperaObjeto(String pOID){
48.
ArrayList vetEnvia = new ArrayList();
49.
this.objFuncionarioHorista =
(FuncionarioHorista)Funcionario.RecuperaObjeto(pOID);
50.
vetEnvia.Add(this.objFuncionarioHorista.Codigo);
51.
vetEnvia.Add(this.objFuncionarioHorista.Nome);
52.
vetEnvia.Add(this.objFuncionarioHorista.CPF);
53.
vetEnvia.Add(this.objFuncionarioHorista.Departamento.Codigo);
54.
vetEnvia.Add(this.objFuncionarioHorista.NumDiasTrabalhados);
55.
vetEnvia.Add(this.objFuncionarioHorista.NumHorasDiaTrabalhado);
56.
vetEnvia.Add(this.objFuncionarioHorista.ValorHora);
57.
return vetEnvia;
58.
}
59.
}
60. }
Na Listagem 5 é apresentada a classe cntrFuncionarioHorista. O primeiro método
apresentado é o Salvar, que é invocado pela interface quando o usuário aciona essa opção no
formulário. Ele recebe como parâmetro um ArrayList com os dados do FuncionarioHorista. É de
responsabilidade da interface, pegar os dados digitados pelo usuário no formulário e montar esse
vetor.
O método então verifica se é uma operação de atualização ou de criação. Caso seja de
atualização é invocado o Atualizar (linha 39) do objeto. Para o outro caso, é acionado o método
Persistir (linha 23). No caso de criação de um novo funcionário, é atribuído ao código do objeto,
um novo OID, usando a estratégia do GUID (linha 16).
O segundo método apresentado é o RecuperaObjeto, que recebe como parâmetro uma string,
representando o OID do objeto que a interface deseja recuperar. O método então invoca o
RecuperaObjeto da classe FuncionarioHorista e monta um vetor com os seus dados e o devolve
para a interface. A interface então pode tratar da forma que lhe for conveniente.
A classe controladora do Departamento, a cntrDepartamento possui definição semelhante as
já apresentadas e por isso não esta sendo definida.
Nota: O código-fonte completo da aplicação esta disponível para download.
Mapeamento Objeto-Relacional e Banco de Dados
O mapeamento objeto-relacional é uma abordagem que permite a construção de sistemas
utilizando o paradigma Orientado a Objetos com a persistência desses objetos em bancos de dados
relacionais. Utilizando-se de técnicas e estratégias específicas, é possível mapear classes com seus
atributos e associações para o modelo relacional.
Utiliza-se uma abstração bastante intuitiva no sentido de que uma classe pode ser mapeada
para uma tabela no banco de dados relacional e atributos da classe para campos da tabela. Porém,
algumas diferenças entre os dois modelos, como o OID, tipos de dados, herança e associações,
demandam um estudo mais detalhado das estratégias de mapeamento.
Para o estudo de caso, a construção da base de dados seguiu esse raciocínio. A classe
Departamento foi mapeada para a tabela Departamento, com seus atributos sendo os campos da
tabela e a hierarquia Funcionario e FuncionarioHorista sendo mapeada para as tabelas Funcionario
e FuncionarioHorista, cada uma com os seus respectivos atributos sendo os campos das tabelas
Para estabelecer a hierarquia, a chave primária da tabela FuncionarioHorista é a mesma chave
primária da tabela Funcionario, estabelecendo assim uma relação de chave primária estrangeira.
Existem outras duas possíveis alternativas para se mapear uma hierarquia: criar uma única
tabela, com todos os atributos como campos da tabela, com um campo adicional chamado tipo, que
representa a classe que instanciou cada objeto ou criar uma nova tabela para cada classe concreta,
contendo os seus atributos, mais os atributos da classe abstrata.
Assim, a Listagem 6 apresenta o script para criação do banco, resultante do mapeamento
objeto-relacional realizado com as classes do estudo de caso. A base de dados utilizada para o
estudo de caso é o SQL SERVER 2005 Express Edition. No entanto, qualquer outra base seria
suportada nesta arquitetura.
Listagem 6. Script para criação das tabelas no banco de dados
CREATE TABLE Departamento(
ID varchar(38) NOT NULL PRIMARY KEY,
Descricao varchar(50) NOT NULL,
)
CREATE TABLE Funcionario(
ID varchar(38) NOT NULL PRIMARY KEY,
Nome varchar(50) NOT NULL,
CPF varchar(14) NOT NULL,
DepartamentoOID varchar(38) NOT NULL,
)
CREATE TABLE FuncionarioHorista(
ID varchar(38) NOT NULL PRIMARY KEY,
NumDiasTrabalhados int NOT NULL,
NumHorasDiaTrabalhado int NOT NULL,
ValorHora int NOT NULL,
)
ALTER TABLE Funcionario ADD CONSTRAINT fk_DPTOOID
FOREIGN KEY(DepartamentoOID) REFERENCES Departamento(ID)
ALTER TABLE FuncionarioHorista ADD CONSTRAINT fk_DPTOOID
FOREIGN KEY(ID) REFERENCES Funcionario(ID)
Classes de Persistência e Manipulação de Objetos
Conforme já apresentado, o namespace Persistence constitui uma importante parte da
aplicação. A parte que é responsável pela persistência e manipulação dos objetos no banco de dados
relacional. Neste são definidas as classes que representam um padrão de projeto chamado DAO –
Data Access Object – que, basicamente, define o encapsulamento do acesso aos dados por meio de
classes especificamente construídas para essa tarefa. Assim, para cada classe persistente do modelo
existe uma classe DAO correspondente.
No entanto, antes de se utilizar os métodos das classes DAO, é necessário se conectar e
autenticar no banco de dados e configurar o catálogo correspondente no SQL SERVER que possui a
estrutura de tabelas já apresentadas. Para isso, dentro do namespace Persistence, foi criada uma
classe com o nome FabricaConexao, com objetivo de centralizar a conexão com a base de dados
em um único lugar e certificando que ela será feita somente uma única vez ao carregar a aplicação.
Essa classe é a implementação de um padrão de projeto criacional chamado Singleton, que
garante que uma classe possua apenas uma única instância fornecendo um ponto global de acesso
para a mesma, através de atributos e definições estáticas (static). Com isso é disponibilizado à
classe um ponto global e único de conexão à base de dados, necessário para a persistência e
manipulação dos objetos de forma eficaz e eficiente. A Listagem 7 apresenta a definição dessa
classe (crie um arquivo CS - FabricaConexao.cs - semelhante aos exemplos anteriores dentro do
namespace Persistence).
Listagem 7. Definição da Classe FabricaConexao
1. using System;
2. using System.Collections.Generic;
3. using System.Text;
4. using System.Data.SqlClient;
5.
6. namespace Empresa.Persistence{
7.
public class FabricaConexao{
8.
private String stringconnection = "Data Source=NOTEBK-DAIBERT\\SQLEXPRESS;
Initial Catalog=Empresa;Integrated Security=SSPI;MultipleActiveResultSets=True";
9.
private static SqlConnection objConexao = null;
10.
11.
public FabricaConexao(){
12.
objConexao = new SqlConnection();
13.
objConexao.ConnectionString = stringconnection;
14.
objConexao.Open();
15.
}
16.
public static SqlConnection getConexao(){
17.
if (objConexao == null){
18.
new FabricaConexao();
19.
}
20.
return objConexao;
21.
}
22.
public static void fecharConexao(){
23.
objConexao.Close();
24.
}
25.
}
26. }
A Listagem 7, define a classe FabricaConexao. Esta classe faz uso, para este caso de uso, da
classe SqlConnection, disponibilizada em System.Data.SqlClient (linha 4). Ela possui dois atributos:
stringconnection, que armazena em uma string a string de conexão para o banco de dados,
configurando o servidor, o catálogo, forma de autenticação, e o atributos objConexao, este do tipo
SqlConnection que é o responsável por realizar a conexão com o banco de dados com os dados
definidos na stringconnection. Entre as linhas 11 e 15 é definido o método construtor da classe. Este
instancia um objeto SqlConnection no atributo objConexao, configura a string de conexão e aciona
o método Open. Este se conecta no banco de dados e mantém um ponteiro na base de dados
autenticada para que aplicação faça uso. Entre as linhas 16 e 21, é definido o método getConexao.
Este disponibiliza o objConexao à aplicação. Observe que este método verifica se o objConexao é
null. Se for, ele invoca o método construtor que por sua vez, como definido, irá instanciar
objConexao. Desta forma, garante-se que esta instancia é única em toda a aplicação. Toda
solicitação de conexão ao banco de dados é centralizada nesta classe a sempre feita através deste
método getConexao. Por fim, na linha 22, é definido o método fecharConexao. Este tem como
objetivo fechar a conexão com o banco de dados, utilizada somente quando a aplicação se finaliza
no estudo de caso.
Com isso, é possível analisar as definições das classes DAO. Como já apresentado, cada
classe do modelo (Funcionario, FuncionarioHorista e Departamento) possui o seu DAO
correspondente. Desta forma, tem-se: FuncionarioDAO, FuncionarioHoristaDAO e
DepartamentoDAO. Todos definidos de forma autônoma, cada um em uma classe dentro do
namespace Persistence. As Listagens 8 e 9 definem as classes FuncionarioDAO e
FuncionarioHoristaDAO respectivamente. Observe que as classes DAO recebem as chamadas das
classes do modelo (classes de domínio) e são as únicas da aplicação que acessam o banco de dados,
respeitando assim o que fora definido referente à programação em camadas. Diferentemente como
observado quando se é utilizado um framework de persistência, como o nHibernate, nesta
abordagem, as classes DAO manipulam os objetos com SQL diretamente em seus métodos.
Novamente, para fins do estudo de caso, é omitida a classe DAO correspondente ao
Departamento, pois esta possui definição muito semelhante às apresentadas.
Listagem 8. Definição da Classe FuncionarioDAO
1.
using System;
2.
using System.Collections;
3.
using System.Text;
4.
using System.Data.SqlClient;
5.
using Empresa.Model;
6.
using Empresa.Controller;
7.
8.
namespace Empresa.Persistence{
9.
class FuncionarioDAO{
10.
public FuncionarioDAO(){
11.
}
12.
public static Funcionario recuperaObjeto(String pID){
13.
SqlConnection objConexao = FabricaConexao.getConexao();
14.
String strQuery = "SELECT ID, Nome, CPF, DepartamentoOID FROM
Funcionario WHERE ID = '"+pID+"'";
15.
SqlCommand objCommand = new SqlCommand(strQuery, objConexao);
16.
cntrDepartamento objControllerDepartamento = new cntrDepartamento();
17.
SqlDataReader objLeitor2;
18.
Funcionario objFuncionario = null;
19.
FuncionarioHorista objFuncionarioHorista;
20.
try{
21.
objCommand.ExecuteNonQuery();
22.
SqlDataReader objLeitor = objCommand.ExecuteReader();
23.
while (objLeitor.Read()){
24.
//Verificar Tipo do Funcionário e Recuperar o Restante do
Funcionário
25.
//Tentar em Todos os Filhos, até encontrar a Chave Primária
Estrangeira Igual
26.
strQuery = "SELECT ID, NumDiasTrabalhados,
NumHorasDiaTrabalhado, ValorHora FROM FuncionarioHorista WHERE ID = '" + objLeitor[0] +
"'";
27.
objCommand = new SqlCommand(strQuery, objConexao);
28.
objCommand.ExecuteNonQuery();
29.
objLeitor2 = objCommand.ExecuteReader();
30.
while (objLeitor2.Read()){
31.
objFuncionarioHorista = new FuncionarioHorista();
32.
objFuncionarioHorista.NumDiasTrabalhados =
Convert.ToInt32(objLeitor2[1]);
33.
objFuncionarioHorista.NumHorasDiaTrabalhado =
Convert.ToInt32(objLeitor2[2]);
34.
objFuncionarioHorista.ValorHora =
Convert.ToInt32(objLeitor2[3]);
35.
objFuncionarioHorista.Codigo =
Convert.ToString(objLeitor[0]);
36.
objFuncionarioHorista.Nome = Convert.ToString(objLeitor[1]);
37.
objFuncionarioHorista.CPF = Convert.ToString(objLeitor[2]);
38.
objFuncionarioHorista.Departamento =
objControllerDepartamento.RecuperaObjetoObjeto(Convert.ToString(objLeitor[3]));
39.
objFuncionario = (Funcionario)objFuncionarioHorista;
40.
}
41.
//Fim Tentativas de Descobrir Tipo Funcionário
42.
}
43.
objLeitor.Close();
44.
return objFuncionario;
45.
}catch (SqlException err){
46.
String strErro = "Erro: " + err.ToString();
47.
Console.Write(strErro);
48.
return objFuncionario;
49.
}
50.
}
51.
public static IList recuperaObjetosPorDepartamento(String pOID){
52.
IList listFuncionarios = new ArrayList();
53.
SqlConnection objConexao = FabricaConexao.getConexao();
54.
String strQuery = "SELECT ID, Nome, CPF, DepartamentoOID FROM
Funcionario WHERE DepartamentoOID = '"+pOID+"'";
55.
SqlCommand objCommand = new SqlCommand(strQuery, objConexao);
56.
cntrDepartamento objControllerDepartamento = new cntrDepartamento();
57.
SqlDataReader objLeitor2;
58.
try{
59.
objCommand.ExecuteNonQuery();
60.
SqlDataReader objLeitor = objCommand.ExecuteReader();
61.
while (objLeitor.Read()){
62.
//Verificar Tipo do Funcionário e Recuperar o Restante do
Funcionário
63.
//Tentar em Todos os Filhos, até encontrar a Chave Primária
Estrangeira Igual
64.
strQuery = "SELECT ID, NumDiasTrabalhados,
NumHorasDiaTrabalhado, ValorHora FROM FuncionarioHorista WHERE ID = '" + objLeitor[0] +
"'";
65.
objCommand = new SqlCommand(strQuery, objConexao);
66.
objCommand.ExecuteNonQuery();
67.
objLeitor2 = objCommand.ExecuteReader();
68.
while (objLeitor2.Read()){
69.
FuncionarioHorista objFuncionario = new
FuncionarioHorista();
70.
objFuncionario.NumDiasTrabalhados =
Convert.ToInt32(objLeitor2[1]);
71.
objFuncionario.NumHorasDiaTrabalhado =
Convert.ToInt32(objLeitor2[2]);
72.
objFuncionario.ValorHora = Convert.ToInt32(objLeitor2[3]);
73.
objFuncionario.Codigo = Convert.ToString(objLeitor[0]);
74.
objFuncionario.Nome = Convert.ToString(objLeitor[1]);
75.
objFuncionario.CPF = Convert.ToString(objLeitor[2]);
76.
objFuncionario.Departamento =
objControllerDepartamento.RecuperaObjetoObjeto(Convert.ToString(objLeitor[3]));
77.
listFuncionarios.Add(objFuncionario);
78.
}
79.
//Fim Tentativas de Descobrir Tipo Funcionário
80.
}
81.
objLeitor.Close();
82.
return listFuncionarios;
83.
}catch (SqlException err){
84.
String strErro = "Erro: " + err.ToString();
85.
Console.Write(strErro);
86.
return listFuncionarios;
87.
}
88.
}
89.
public static IList recuperaObjetos(){
90.
IList listFuncionarios = new ArrayList();
91.
SqlConnection objConexao = FabricaConexao.getConexao();
92.
String strQuery = "SELECT ID, Nome, CPF, DepartamentoOID FROM
Funcionario";
93.
SqlCommand objCommand = new SqlCommand(strQuery, objConexao);
94.
cntrDepartamento objControllerDepartamento = new cntrDepartamento();
95.
SqlDataReader objLeitor2;
96.
try{
97.
objCommand.ExecuteNonQuery();
98.
SqlDataReader objLeitor = objCommand.ExecuteReader();
99.
while (objLeitor.Read()) {
100.
//Verificar Tipo do Funcionário e Recuperar o Restante do
Funcionário
101.
//Tentar em Todos os Filhos, até encontrar a Chave Primária
Estrangeira Igual
102.
strQuery = "SELECT ID, NumDiasTrabalhados,
NumHorasDiaTrabalhado, ValorHora FROM FuncionarioHorista WHERE ID = '"+objLeitor[0]+"'";
103.
objCommand = new SqlCommand(strQuery, objConexao);
104.
objCommand.ExecuteNonQuery();
105.
objLeitor2 = objCommand.ExecuteReader();
106.
while (objLeitor2.Read()){
107.
FuncionarioHorista objFuncionario = new
FuncionarioHorista();
108.
objFuncionario.NumDiasTrabalhados =
Convert.ToInt32(objLeitor2[1]);
109.
objFuncionario.NumHorasDiaTrabalhado =
Convert.ToInt32(objLeitor2[2]);
110.
objFuncionario.ValorHora = Convert.ToInt32(objLeitor2[3]);
111.
objFuncionario.Codigo = Convert.ToString(objLeitor[0]);
112.
objFuncionario.Nome = Convert.ToString(objLeitor[1]);
113.
objFuncionario.CPF = Convert.ToString(objLeitor[2]);
114.
objFuncionario.Departamento =
objControllerDepartamento.RecuperaObjetoObjeto(Convert.ToString(objLeitor[3]));
115.
listFuncionarios.Add(objFuncionario);
116.
}
117.
//Fim Tentativas de Descobrir Tipo Funcionário
118.
}
119.
objLeitor.Close();
120.
return listFuncionarios;
121.
}
122.
catch (SqlException err){
123.
String strErro = "Erro: " + err.ToString();
124.
Console.Write(strErro);
125.
return listFuncionarios;
126.
}
127.
}
128.
public static Boolean excluir(String pID){
129.
SqlConnection objConexao = FabricaConexao.getConexao();
130.
//Criar query para deletar o funcionario associado. Criar query para
todos os tipos.
131.
String strQuery = "DELETE FROM Funcionario WHERE ID = '" + pID + "'";
132.
strQuery = strQuery + "; DELETE FROM FuncionarioHorista WHERE ID = '"+
pID +"'";
133.
//Caso tivesse mais tipos, adicionar a query para todos os tipos, por
exemplo FuncionarioDiarista caso existisse:
134.
//strQuery = strQuery + "; DELETE FROM FuncionarioDiarista WHERE ID = '"
+ pID + "'";
135.
//Desta forma todo tipo de funcionário seria deletado, sem que seja
necessário descobrir o seu tipo específico. A query não retorna erro quando não encontra
o objeto na clausula WHERE
136.
SqlCommand objCommand = new SqlCommand(strQuery, objConexao);
137.
try{
138.
objCommand.ExecuteNonQuery();
139.
return true;
140.
}catch (SqlException err){
141.
String strErro = "Erro: " + err.ToString();
142.
Console.Write(strErro);
143.
return false;
144.
}
145.
}
146.
public static int ContabilizaFuncionariosPorDepartamento(String
pIDDepartamento) {
147.
SqlConnection objConexao = FabricaConexao.getConexao();
148.
String strQuery = "SELECT COUNT(*) FROM Funcionario WHERE
DepartamentoOID = '"+pIDDepartamento+"'";
149.
SqlCommand objCommand = new SqlCommand(strQuery, objConexao);
150.
int vRetorno = 0;
151.
try{
152.
objCommand.ExecuteNonQuery();
153.
SqlDataReader objLeitor = objCommand.ExecuteReader();
154.
while (objLeitor.Read())
155.
{
156.
vRetorno = Convert.ToInt32(objLeitor[0]);
157.
}
158.
return vRetorno;
159.
}catch (SqlException err){
160.
String strErro = "Erro: " + err.ToString();
161.
Console.Write(strErro);
162.
return vRetorno;
163.
}
164.
}
165.
}
166. }
Em toda a estrutura DAO, existem basicamente os seguintes métodos: recuperaObjeto
(recupera um objeto em específico da classe referenciada), recuperaObjetos (recupera todos os
objetos da classe referenciada), persistir (persiste um objeto no banco de dados), atualizar (atualiza
um objeto em específico com novas informações) e excluir (excluir um objeto especifico). Outros
métodos são definidos, mas as chamadas operações CRUD (CREATE, RETRIEVE, UPDATE e
DELETE) são feitas por estes apresentados. O CRUD representa todas as operações básicas
necessárias para a persistência e manipulação de objetos em uma base de dados relacional.
A Listagem 8 inicia importando as bibliotecas e os namespaces que fará uso. Isso ocorre
entre as linhas 1 e 6. Da linha 12 até a 50 é definido o método recuperaObjeto. Este método recebe
por parâmetro o atributo identificador de um Funcionário que ao final do método é recuperado da
base de dados e disponibilizado à aplicação. Na linha 13 é feito a solicitação à classe
FabricaConexao pelo objeto de conexão. Com este, é possível estabelecer consultar no banco de
dados configurado. Na linha 14, é definida uma variável chamada strQuery. Esta variável armazena
a consulta SQL que será enviada para o banco de dados. Observe que neste caso esta sendo feito um
SELECT, solicitando os campos ID, Nome, CPF, DepartamentoOID da tabela Funcionario que
tenha o ID passado por parâmetro. Após, na linha 15, é instanciado um objeto do tipo SqlCommand,
responsável por executar a consulta na base de dados, configurada pela conexão recebida pelo
FabricaConexao. Entre as linhas 23 e 44, é feito um esforço para recuperar as informações da base
de dados e então popular um objeto, que é então retornado como retorno do método. Na linha 38, é
solicitado ao controlador de Departamento que recupere o objeto Departamento associado ao
Funcionário, já que todo funcionário deve estar alocado em um departamento. O método
RecuperaObjetoObjeto, da classe cntrDepartamento é um método de classe que retorna um objeto
Departamento de acordo com o parâmetro passado. A partir da linha 23 o método busca identificar
o tipo do funcionário para recuperar todas as informações e que seja instanciado um objeto de forma
correta da hierarquia.
Entre as linhas 89 e 127 é definido o método recuperaObjetos. Este recupera todos os objetos
da base de dados. O seu funcionamento é semelhante ao recuperaObjeto, mas com uma diferença:
não é retornado um objeto, mas sim uma lista de objetos – todos os persistidos na base de dados. O
tipo de retorno do método é IList, que é a interface da classe Collections. O que permite a utilização
da classe ArrayList, como definido na linha 90. Já entre as linhas 128 e 145 é apresentado o método
excluir. Este recebe o atributo identificador do funcionário como parâmetro e o excluir da base de
dados, retornando verdadeiro em caso de sucesso e falso em caso de insucesso.
Nas linhas 146 à 164 é definido o método ContabilizaFuncionariosPorDepartamento. Este
método faz uma contagem no banco de dados de quantos funcionários existem em um determinado
departamento. Por fim, entre as linhas 51 e 88 é apresentado o método
recuperaObjetosPorDepartamento. Este responsável por recuperar uma lista de funcionários de um
determinado departamento. Para isso é feita uma consulta restritiva no campo DepartamentoOID,
como pode ser observado na linha 54.
Listagem 9. Definição da Classe FuncionarioHoristaDAO
1. using System;
2. using System.Collections.Generic;
3. using System.Text;
4. using Empresa.Model;
5. using System.Data.SqlClient;
6.
7. namespace Empresa.Persistence{
8.
class FuncionarioHoristaDAO{
9.
public FuncionarioHoristaDAO(){
10.
}
11.
public Boolean persistir(FuncionarioHorista pFunHorista){
12.
SqlConnection objConexao = FabricaConexao.getConexao();
13.
String strQuery = "INSERT INTO Funcionario VALUES('" + pFunHorista.Codigo
+ "','" + pFunHorista.Nome + "','"+ pFunHorista.CPF
+"','"+pFunHorista.Departamento.Codigo+"')";
14.
strQuery = strQuery + "; INSERT INTO FuncionarioHorista VALUES
('"+pFunHorista.Codigo+"','"+pFunHorista.NumDiasTrabalhados+"','"+pFunHorista.NumHorasDia
Trabalhado+"','"+pFunHorista.ValorHora+"')";
15.
SqlCommand objCommand = new SqlCommand(strQuery, objConexao);
16.
try{
17.
objCommand.ExecuteNonQuery();
18.
return true;
19.
}catch (SqlException err){
20.
String strErro = "Erro: " + err.ToString();
21.
Console.Write(strErro);
22.
return false;
23.
}
24.
}
25.
public Boolean atualizar(FuncionarioHorista pFunHorista){
26.
SqlConnection objConexao = FabricaConexao.getConexao();
27.
String strQuery = "UPDATE Funcionario SET Nome = '" + pFunHorista.Nome +
"', CPF = '"+pFunHorista.CPF+"', DepartamentoOID = '"+pFunHorista.Departamento.Codigo+"'
WHERE ID = '" + pFunHorista.Codigo + "'";
28.
strQuery = strQuery + "UPDATE FuncionarioHorista SET NumDiasTrabalhados =
'"+pFunHorista.NumDiasTrabalhados+"', NumHorasDiaTrabalhado =
'"+pFunHorista.NumHorasDiaTrabalhado+"', ValorHora = '"+pFunHorista.ValorHora+"' WHERE ID
= '"+pFunHorista.Codigo+"'";
28.
SqlCommand objCommand = new SqlCommand(strQuery, objConexao);
30.
try{
31.
objCommand.ExecuteNonQuery();
32.
return true;
33.
}catch (SqlException err){
34.
String strErro = "Erro: " + err.ToString();
35.
Console.Write(strErro);
36.
return false;
37.
}
38.
}
39.
}
40. }
A classe FuncionarioHoristaDAO esta sendo apresentada pela Listagem 9. Em estrutura
semelhante à classe FuncionarioDAO, esta apresenta dois métodos: persistir e atualizar. O persistir
é responsável por salvar um objeto FuncionarioHorista no banco de dados e o atualizar por
atualizá-lo. Em cada um dos métodos são definidos um SQL referente a cada operação. Em ambos
os casos, é retornado verdadeiro em caso de sucesso e falso em caso de insucesso.
Buscando exemplificar o funcionamento da arquitetura do sistema proposto, é apresentado de
forma detalhada a funcionalidade Cadastrar Funcionário Horista. A Figura 6 exibe o diagrama de
seqüência, com o objetivo de nortear os passos realizados para esse cenário.
Figura 6. Diagrama de Seqüência para a Funcionalidade Cadastrar Funcionário Horista
Nota: O código-fonte completo da aplicação está disponível para download.
O caso de uso inicia, quando o usuário acessa a interface e aciona a opção Adicionar Novo
Funcionário Horista. Esse por sua vez acessa o controlador da classe Departamento
(cntrDepartamento) para alimentar o ComboBox com todos os departamentos, invocando o
RecuperaObjetos. Esse, por sua vez, aciona o RecuperaObjetos da classe Departamento, que acessa
o método recuperaObjetos da classe DepartamentoDAO para recuperar os departamentos da base
de dados.
Com o ComboBox montado, é exibido o formulário de criação de um novo funcionário como
exemplificado na Figura 7.
Figura 7. Interface do formulário de um novo funcionário horista
O usuário informa então os dados do novo funcionário e aciona o Salvar. Nesse ponto, o
formulário (frmNovoFuncionarioHorista) monta um vetor (vetEnvia) com os dados do funcionário
e passa como parâmetro ao Salvar do controlador da classe FuncionarioHorista
(cntrFuncionarioHorista).
Esse, por sua vez, instancia um novo objeto do tipo FuncionarioHorista, e atribui os valores
do vetor ao objeto. Com o objeto montado, é invocado o seu método Persistir. Este faz o acesso à
classe FuncionarioHoristaDAO, que inicialmente resgata da classe FabricaConexao uma nova
seção de conexão com o banco de dados (getConexao) e gera o SQL para inserir as novas
informações na base de dados. Este então executa o SQL na base de dados persistindo o objeto.
Caso todas as operações tenham sido realizadas com sucesso, é retornado true para todos os
métodos até a interface. Caso tenha ocorrido algum erro, é retornado false para a interface que se
responsabiliza em emitir uma mensagem de erro ao usuário.
Somente a interface tem acesso ao usuário nessa abordagem, portanto, somente ela pode
enviar notificações de erros ou de sucesso. Após, uma consulta ao banco de dados pode verificar a
persistência dos objetos.
Conclusão
Este artigo teve como objetivo abordar de forma prática a utilização do paradigma Orientado
a Objetos no contexto do desenvolvimento de software no ambiente do Visual Studio na linguagem
C# .NET e de técnicas aliadas, como mapeamento objeto relacional e utilização de padrões de
desenvolvimento.
Foi possível observar na prática como realizar de forma simples a persistência e manipulação
de objetos utilizando um banco de dados relacional, apoiado por padrões de desenvolvimento, como
o padrão de projeto DAO (Data Access Object), que encapsula todo o contexto de conexão e
comunicação com o banco de dados.
Atualmente, essa abordagem tem uma grande aceitação, uma vez que os bancos de dados
Orientados a Objetos ainda não são o padrão de mercado, tornando possível a união entre os
conceitos da Orientação a Objetos, com o desempenho, maturidade e confiabilidade dos bancos de
dados relacionais.
Download