.NET - Apresentando o padrão DAO - Data Access Object


Você conhece o padrão de projeto DAO - Data Access Object ?

Se você trabalha com aplicações com acesso a dados talvez já tenha até usado o padrão em seu código sem saber; mas hoje você será apresentado formalmente ao padrão DAO.

O padrão DAO é um padrão de projeto que abstrai e encapsula os mecanismos de acesso a dados escondendo os detalhes da execução da origem dos dados.

Já deu para notar que esse padrão é muito usado em aplicações que utilizam banco de dados relacionais.

No princípio era o caos

Não é muito difícil encontrar na web exemplos de código na linguagem VB .NET ou C# que mostram como fazer o acesso a um banco de dados relacional e realizar as tão conhecidas operações CRUD (Create, Update e Delete).

O problema é que em grande parte dos exemplos que são apresentados geralmente o código esta embutido no formulário ou página da aplicação.

Tomemos como exemplo um formulário ou página de cadastro onde são realizadas as operações para incluir, alterar, atualizar e excluir registros de uma tabela de um banco de dados.

Geralmente esses exemplos trazem o código de acesso e persistência de dados no próprio formulário e de forma geral você terá espalhado pela interface da aplicação trechos de código como o abaixo que fazem o acesso aos dados :

.....
Dim conexao As OleDbConnection
Dim myCommand As OleDbCommand

conexao = New OleDbConnection("PROVIDER=Microsoft.Jet.OLEDB.4.0;DATASource=c:\Teste.mdb" )
conexao.Open()
comando = New OleDbCommand( "Insert INTO Teste ( Nome ) Values ( 'Macoratti' )", conexao )
comando.ExecuteNonQuery()
conexao.Close()
...........

A 'arquitetura' usada neste modelo pode ser vista na figura abaixo:

Mas essa abordagem fere um dos princípios básicos das boas práticas : a separação das responsabilidades.

As boas práticas recomendam que a camada de interface não deve conhecer particularidades de acesso a dados, e, que essa responsabilidade deve estar concentrada em uma camada separada e independente. Desta forma a camada de interface, aqui representada pela aplicação Windows Forms, não deveria ter código de acesso a dados e deveríamos criar uma outra camada contendo o código cuja responsabilidade seria realizar as tarefas pertinentes ao acesso a dados

Essa prática é adotada por ser mais rápida e intuitiva de ser feita mas traz grandes desvantagens como:

Para resolver este problema temos o padrão DAO que é um dos muitos padrões que podemos usar para desacoplar o código de acesso e persistência dos dados da lógica da aplicação. O objetivo do padrão é tornar o código mais organizado, fácil de manter e entender e de poder ser reutilizado em outras aplicações.

O padrão DAO - Data Access Object

Este padrão permite criar as classes de dados independentemente da fonte de dados ser um BD relacional, um arquivo texto, um arquivo XML, etc. Para isso, ele encapsula os mecanismos de acesso a dados e cria uma interface de cliente genérica para fazer o acesso aos dados permitindo que os mecanismos de acesso a dados sejam alterados independentemente do código que utiliza os dados.

Existem diversas implementações do padrão DAO mas em geral podemos relacionar algumas características desejáveis em uma implementação do padrão DAO:

- Todo o acesso aos dados deve ser feita através das classes DAO de forma a se ter o encapsulamento;
- Cada instância da DAO é responsável por um objeto de domínio;
- O DAO deve ser responsável pelas operações CRUD no domínio;
- O DAO não deve ser responsável por transações , sessões ou conexões que devem ser tratados fora do DAO;

Nas figura abaixo vemos o diagrama de classe mostrando os relacionamentos para o padrão DAO e o diagrama de sequência mostrando a interação entre os participantes:
(fonte: http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html)

  • A classe DataAccessObject encapsula o acesso aos dados; contém os mapeamentos;
  • A classe DataSource é a origem dos dados: BD, XML, etc.;
  • A classe BusinessObject contém a lógica de negócio e usa o objeto DataAccessObject;
  • Representa o objeto de domínio: Cliente,Produto,etc;
    Contém os dados que transitam de/para a fonte de dados;

Diagrama DAO

  • Diagrama de sequência DAO

Na figura abaixo vemos o diagrama para uma implementação do padrão DAO:

A classe CustomerDAO (DataAccessObject) é a classe concreta que implementa a interface ICustomerDAO sendo responsável pelo acesso e persistência dos dados do cliente;

A classe Customer é a classe do domínio e representa um cliente;

Exemplo de código VB .NET e C# para a interface IClienteDAO (DataAccessObject):

Vamos definir na interface o código conforme abaixo:

Public Interface IClienteDAO(Of T)

    Function ExibirTodos() As List(Of T)
    Sub Gravar(ByVal obj As T)
    Function Consultar(ByVal nome As String) As DataTable 
End Interface
public interface IClienteDAO<T>
{
   List<T> ExibirTodos(  );
   void Gravar(T obj);                                                        
   DataTable Consultar(string nome);
}

Exemplo de código para definição da classe Cliente (TransferObject)

Public Class Cliente

Private _id As Integer
Private _nome As String

Public Property Codigo As Integer

Get
     Return _id
End Get
Set(ByVal value As Integer)
    _id = value
End Set
End Property

Public Property NomeCliente As String
Get
    Return _nome
End Get
Set(ByVal value As String)
     _nome = value
End Set
End Property
End Class

public class Cliente
{

private int _id;

private string _nome;
public int Codigo {
get { return _id; }
set { _id = value; }
}

public string NomeCliente {
get { return _nome; }
set { _nome = value; }
}
}

A classe Cliente apresenta somente 2 propriedades :

Exemplo de código para a classe ClienteDAO que implementa a interface IClienteDAO:

Imports System.Data
Imports System.Data.SqlClient
Public Class ClienteDAO
    Implements IClienteDAO(Of Cliente)

    Private Shared ReadOnly instancia As New ClienteDAO()

    Sub New()
    End Sub
    Public Shared Function GetInstance() As ClienteDAO
        Return instancia
    End Function
    Public Function Consultar(ByVal nome As String) As DataTable 
Implements IClienteDAO(Of Cliente).Consultar
        Try
            Using con As SqlConnection = ConexaoBD.GetInstancia.GetConnection()
                Try
                    con.Open()
                    Dim sql As String = ("Select nome, idade from clientes where nome = '" & nome & "'")
                    Dim cmd As SqlCommand = New SqlCommand(sql, con)
                    Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)
                    Dim cliente As DataTable = New DataTable
                    da.Fill(cliente)
                    Return cliente
                Catch ex As SqlException
                    Throw ex
                Finally
                    con.Close()
                End Try
            End Using
        Catch ex As Exception
            Throw ex
        End Try
    End Function

    Public Sub Gravar(ByVal cliente As Cliente) 
Implements IClienteDAO(Of Cliente).Gravar
        Try
            Using con As SqlConnection = ConexaoBD.GetInstancia.GetConnection()
                Try
                    con.Open()
                    Dim cmd As SqlCommand = New SqlCommand()
                    cmd.Connection = con
                    cmd.CommandText = "INSERT INTO  Clientes (nome, idade) values (@nome, @idade)"
                    Dim parNome As SqlParameter = New SqlParameter("@nome", cliente.NomeCliente)
                    Dim parIdade As SqlParameter = New SqlParameter("@idade", cliente.IdadeCliente)
                    cmd.Parameters.Add(parNome)
                    cmd.Parameters.Add(parIdade)
                    cmd.ExecuteNonQuery()
                Catch ex As SqlException
                    Throw ex
                Finally
                    con.Close()
                End Try
            End Using
        Catch ex As Exception
            Throw ex
        End Try
    End Sub
    Public Function ExibirTodos() As List(Of Cliente) 
Implements IClienteDAO(Of Cliente).ExibirTodos
        Try
            Using con As SqlConnection = ConexaoBD.GetInstancia.GetConnection()
                Try
                    con.Open()
                    Dim sql As String = ("Select Id,nome, idade from clientes")
                    Dim listaClientes As IList(Of Cliente) = New List(Of Cliente)
                    Dim cmd As SqlCommand = New SqlCommand(sql, con)
                    Dim dr As SqlDataReader = cmd.ExecuteReader
                    While (dr.Read)
                        Dim cliente As New Cliente
                        cliente.Codigo = CLng(dr("Id"))
                        cliente.NomeCliente = dr("nome")
                        cliente.idadeCliente = CInt(dr("idade"))
                        listaClientes.Add(cliente)
                    End While
                    Return listaClientes
                Catch ex As SqlException
                    Throw ex
                Finally
                    con.Close()
                End Try
            End Using
        Catch ex As Exception
            Throw ex
        End Try
    End Function
End Class
Linguagem Visual Basic .NET

public class ClienteDAO : IClienteDAO<Cliente>
{

   private static readonly ClienteDAO instancia = new ClienteDAO();
   public ClienteDAO()
   {
   }
   public static ClienteDAO GetInstance()
   {
  return instancia;
   }
   public DataTable Consultar(string nome)
   {
try {
using (SqlConnection con = ConexaoBD.GetInstancia.GetConnection()) {
try {
  con.Open();
string sql = ("Select nome, idade from clientes where nome = '" + nome + "'");
SqlCommand cmd = new SqlCommand(sql, con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable cliente = new DataTable();
da.Fill(cliente);
return cliente;
} catch (SqlException ex) {
throw ex;
} finally {
    con.Close();
}
}
} catch (Exception ex) {
    throw ex;
}
}

public void Gravar(Cliente cliente)
{
try {
using (SqlConnection con = ConexaoBD.GetInstancia.GetConnection()) {
try {
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "INSERT INTO  Clientes (nome, idade) values (@nome, @idade)";

SqlParameter parNome = new SqlParameter("@nome", cliente.NomeCliente);
SqlParameter parIdade = new SqlParameter("@idade", cliente.IdadeCliente);
cmd.Parameters.Add(parNome);
cmd.Parameters.Add(parIdade);
cmd.ExecuteNonQuery();
} catch (SqlException ex) {
throw ex;
} finally {
con.Close();
}
}
} catch (Exception ex) {
throw ex;
}
}

public List<Cliente> ExibirTodos()
{
try {
using (SqlConnection con = ConexaoBD.GetInstancia.GetConnection()) {
try {
con.Open();
string sql = ("Select Id,nome, idade from clientes");
IList<Cliente> listaClientes = new List<Cliente>();
SqlCommand cmd = new SqlCommand(sql, con);
SqlDataReader dr = cmd.ExecuteReader();

while ((dr.Read()))
{
Cliente cliente = new Cliente();
cliente.Codigo = Convert.ToInt64(dr["Id"]);
cliente.NomeCliente = dr["nome"];
cliente.idadeCliente = Convert.ToInt32(dr["idade"]);
listaClientes.Add(cliente);
}
return listaClientes;
} catch (SqlException ex) {
throw ex;
} finally {
con.Close();
}
}
} catch (Exception ex) {
throw ex;
}
}
}

LInguagem C#

Lembrando que o padrão DAO não depende da linguagem de programação usada e pode ser implementado em qualquer linguagem, e que existem diversas implementações possíveis.

"E disse-lhes : Ide por todo o mundo, pregai o evangelho a toda a criatura. Quem crer e for batizado será salvo; mas quem não crer será condenado." Marcos 16:15-16

Referências:


José Carlos Macoratti