.NET Magazine edição 60
Esse artigo faz parte da revista .NET Magazine edição 60. Clique aqui para ler todos os artigos desta edição

Boas Práticas - Expert

Programação Orientada a Objetos

Uma abordagem utilizando C# .NET e Padrões de Projeto

 

Com o passar dos anos foi possível observar uma profunda modificação na forma de desenvolvimento de sistemas informatizados, onde se observa uma grande evolução para se alcançar o atual patamar das técnicas e metodologias em utilização atualmente.

Na área da engenharia de software não foi diferente. O desenvolvimento de software amadureceu e ainda deve amadurecer bastante com o passar dos anos. 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 de software. 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 construção de produtos de software, com suporte às 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 MS-SQL 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 é portar transparentemente os objetos de uma aplicação para a base de dados relacional, e vice-versa, de forma genérica.

Para isso, são utilizadas 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.

Na edição 34 da revista .NET Magazine, antes chamada de MSDN Magazine, foi publicado um artigo onde o estudo de caso apresentou a utilização de um framework de persistência chamado NHibernate. Utilizando o raciocínio, este artigo apresenta o mesmo estudo de caso apresentado, mas com a diferença de que 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.

 

Nota do Devman

Padrões de Projeto (Design Patterns): são soluções elegantes e reutilizáveis para problemas recorrentes no desenvolvimento de software Orientado a Objetos.

O conceito de padrões de projetos como é conhecido hoje ganhou popularidade com o livro “Padrões de Projeto: Elementos de Software Orientado a Objetos Reusável” publicado em 1995 pelos autores Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides. Estes autores ficaram conhecidos como “Gangue dos Quatro”, do inglês “Gang of Four” ou “GoF”.

Os padrões “GoF” são organizados em categorias: de criação, estruturas e comportamentais. Os de criação são relacionados à criação de objetos, os estruturas tratam das associações entre classes e objetos e os comportamentais das interações e divisões de responsabilidades entre as classes ou objetos.

 

Estudo de Caso

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

 

imagem

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 utilizando o padrão 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 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 no banco. A Figura 2 representa as camadas do estudo de caso e como é feita a comunicação entre elas.

 

imagem 

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 e estes, por sua vez, com as 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 entre elas. 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 estas, por sua vez, fazem acesso ao banco de dados e efetivamente fazem 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.

 

imagem

Figura 3. Namespaces do projeto do estudo de caso

 

As Listagens 1, 2 e 3 apresentam, respectivamente, o código-fonte de definição das classes Funcionario, FuncionarioHorista e Departamento, 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.  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.      public abstract class Funcionario{

9.          public Funcionario(String pNome, String pCPF){

10.             this.Nome = pNome;

11.             this.CPF = pCPF;

12.             this.Departamento = new Departamento();

13.         }

14.         public Funcionario(){

15.         }

16.

17.         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.           public static int ContabilizarFuncionariosPorDepartamento(String pIDDepartamento)

57.         {

58.            return FuncionarioDAO.ContabilizaFuncionariosPorDepartamento(pIDDepartamento);

59.         }

60.     }

61. }

 

Na Listagem 1, a linha 8 exibe a assinatura da classe abstrata Funcionario. Ela é abstrata por não permitir ser instanciada, 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. 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 seu 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 será redefinido nas subclasses, de acordo com a necessidade específica de cada tipo de funcionário. 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 as subclasses a implementá-lo.

Entre as linhas 39 e 59 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 das classes DAO, como apresentado anteriormente.

Observe que são definidos alguns métodos com o modificador static como, por exemplo, na linha 45. Este indica um método de classe e não é necessário instanciar um objeto para que o mesmo seja invocado.

 

Nota: Método de classe é um método em que não há a necessidade de 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 à instanciação de um 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; }

...

Quer ler esse conteúdo completo? Tenha acesso completo