.NET - Usando interfaces e o padrão factory para ter um baixo acoplamento


Neste artigo, vamos tentar entender como podemos usar interfaces e o padrão factory para criar uma estrutura em uma aplicação usando uma arquitetura com baixo acoplamento.

Como exemplo eu vou partir de uma arquitetura de três camadas simples, visto que é um modelo muito usado, e aplicar interfaces e o padrão factory para ver como podemos obter um baixo acoplamento em nosso projeto.

Antes de continuar uma pergunta: Você saberia me dizer o que significa para um projeto ter um baixo acoplamento ?

Se não sabe, vamos recordar...

Acoplamento

- Acoplamento é o nível de dependência/conhecimento que pode existir entre as classes;
- Uma classe com acoplamento fraco não é dependente de muitas classes para fazer o que ele tem que fazer;
- Uma classe com acoplamento forte depende de muitas outras classes para fazer o seu serviço;
- Uma classe com
acoplamento forte é mais difícil de manter, de entender e de ser reusada;

Arquitetura em Camadas

Em uma arquitetura em 3 camadas simples temos a separação das responsabilidades onde geralmente temos uma camada de negócios (Business Logic Layer), uma camada de acesso a dados(Data Access Layer) e a camada de apresentação (UI).

Na arquitetura com 3 camadas a camada de apresentação chama a camada de negócios que chama a camada acesso a dados.

Observe que em nenhum dos casos estamos fazendo o acesso direto ao banco de dados pois sempre estamos trabalhando com uma camada de acesso a dados.

Usando uma camada de acesso a dados(DAL) podemos retornar um DataSet, um DataTable ou objetos e tratar o retorno na camada de apresentação.

Com uma camada de negócios (BLL) podemos efetuar efetuar uma validação de regra de negócio e em seguida passar os objetos para a camada de apresentação.

Estes cenários são perfeitamente realistas e aplicáveis e ambos reduzem a quantidade de código e apresentam um melhor desempenho do que a arquitetura one-button-click, aquela velha técnica de colocar um botão de comando e no seu evento Click incluir todo o código necessário para validação , acesso a dados, etc.

A arquitetura em 3 camadas é uma arquitetura cliente servidor na qual a interface do usuário, processos de negócios e armazenamento de dados são desenvolvidos e mantidos em módulos independentes, ou em plataforma separadas.

Em uma arquitetura em 3 camadas basicamente temos as seguintes camadas :
- Camada de apresentação -User Interface (UI)
- Camada de Negócios - Business Logic Layer (BLL)
- Camada de Acesso a dados - Data Access Layer (DAL)

Em se adotarmos uma arquitetura em camadas temos todos os nossos problemas resolvidos ?

Depende...

Vamos analisar cada uma das camadas sob o ponto de vista da estabilidade do código , ou seja, da probabilidade com que cada camada pode sofrer alterações.

A figura abaixo mostra de forma genérica a estabilidade do código em camada:

Podemos ver que a camada de negócios (BLL) é a menos estável pois as regras de negócio tendem a mudar com mais frequência;

A camada de interface (UI) é segunda menos estável visto que poderá haver uma demanda para alteração da interface para dar suporte a outros clientes : web, mobile, desktop. Mas essa mudança não é tão urgente com uma mudança na camada de negócios que exige um pronto atendimento.

A camada de acesso a dados (DAL) é menos volátil das camadas visto que em geral não se muda o banco de dados, e, se isso for realmente necessário pode ser tratado como um projeto de migração. Temos portanto uma mudança planejada que pode esperar. (pelo menos deveria ser assim...)

Dai concluímos que a camada de negócios em geral é que esta mais suscetível a mudanças.

Mas aonde eu quero chegar com tudo isso ???

Controlando a propagação de mudanças

Quando adotamos uma arquitetura em 3 camadas geralmente trabalhamos com as camadas : UI, BLL e DAL , conforme mostrei acima.

Mas seria isso suficiente para que minha aplicação tenha um baixo acoplamento ?

Depende...

Sim, depende como você implementou a sua arquitetura em camadas.

Vejamos um cenário muito comum usado nestes casos.

Vamos criar um projeto no Visual Basic 2010 Express Edition para acessar um banco de dados SQL Server usando 3 Camadas com o nome BaixoAcoplamento;

Abaixo vemos a estrutura da solução contendo 3 Projetos:

A solução BaixoAcoplamento contém 3 projetos:
  • BLL - Camada de Negócios
  • DAL - Camada de Acesso aos Dados
  • UI - Camada de Interface

É importante lembrar que temos que incluir as seguintes referências na solução:

  1. No projeto BLL temos que incluir uma referência ao projeto DAL
  2. No projeto UI temos que incluir uma referência ao projeto BLL

Para fazer isso clique na solução com o botão direito e selecione Properties e a seguir em Common Properties clique em Project Dependencies e a seguir defina as referências conforme a figura abaixo:

Da mesma forma temos que definir qual o projeto deverá dar a partida da solução clicando na opção Startup Project e selecionando o projeto UI;

Feito isso vamos criar a classe ClienteBLL.vb no projeto BLL e a classe AcessoDB.vb no projeto DAL;

Imports DAL
Imports System.Data

Public Class clienteBLL

    Public Function GetCliente() As DataTable 
        Dim dt As New DataTable
        Const stringSQL As String = "SELECT * from Clientes"
        Dim dal As AcessoDB = New AcessoDB()
        dt = dal.obterCliente(stringSQL)
        Return dt
    End Function

    Public Sub IncluirCliente(ByVal strsql As String)
        Dim dal As AcessoDB = New AcessoDB()
        dal.incluir(strsql)
    End Sub
End Class
 
Imports System.Data
Imports System.Data.SqlClient

Public Class AcessoDB

    Dim connectionString As String = "DataSource=.\SQLEXPRESS;InitialCatalog=Clientes;"

    Public Function obterCliente(ByVal Sql As String) As DataTable
        Dim conn As New SqlConnection(connectionString)
        Dim da As New SqlDataAdapter
        Dim dt As New DataTable
        conn.Open()
        Dim cmd As New SqlCommand(Sql, conn)
        cmd.ExecuteNonQuery()
        da.SelectCommand = cmd
        da.Fill(dt)
        conn.Close()
        Return dt
    End Function

    Public Sub incluir(ByVal sql As String)
        Dim conn As New SqlConnection(connectionString)
        Dim cmd As New SqlCommand(sql, conn)
        cmd.ExecuteNonQuery()
        conn.Close()
    End Sub

End Class

1- Código da Classe ClienteBLL

 

2- Código da Classe AcessoDB

Temos acima duas classes concretas.

Agora no projeto da camada de apresentação - UI vamos definir no formulário o seguinte código:

Imports BLL

Public Class Form1

    Dim cliBLL As New clienteBLL
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        DataGridView1.DataSource = cliBLL.GetCliente()
    End Sub

    Private Sub btnIncluir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnIncluir.Click
        Dim strSql As String = "Insert into Clientes Values ( '" & txtNome.Text & "','" & txtFone.Text & "')"
        cliBLL.IncluirCliente(strSql)
    End Sub
End Class

O que vemos nesse código ?

Uma instrução new que no caso indica a criação de um a instância da classe ClienteBLL da camada de negócios onde o objeto criado usa os métodos GetCliente() e IncluirCliente() da camada de negócios.

Isso também indica um forte acoplamento com a camada de negócios e a como a camada de negócios esta mais sujeita a mudanças qualquer alteração nesta camada vai se propagar para a camada de apresentação.

Conclusão : isso não é bom !

Vamos resolver esse problema usando interfaces e o padrão factory.

Primeiro vamos criar uma interface chamada IClienteBLL na camada de negócios com o código a seguir:

Public Interface IClienteBLL

    Function GetCliente() As DataTable
    Sub IncluirCliente(ByVal strsql As String)

End Interface

Nesta interface declaramos um contrato definindo o método GetCliente() e a rotina IncluirCliente() que deverão ser implementados pela classe que utilizar a interface.

A seguir vamos alterar a classe concreta ClienteBLL de forma que ela implemente esta interface. Dessa forma teremos o código a seguir:

Imports DAL
Imports System.Data

Public Class clienteBLL
    Implements IClienteBLL

    Public Function GetCliente() As DataTable Implements IClienteBLL.GetCliente
        Dim dt As New DataTable
        Const stringSQL As String = "SELECT * from Clientes"
        Dim dal As AcessoDB = New AcessoDB()
        dt = dal.obterCliente(stringSQL)
        Return dt
    End Function

    Public Sub IncluirCliente(ByVal strsql As String) Implements IClienteBLL.IncluirCliente
        Dim dal As AcessoDB = New AcessoDB()
        dal.incluir(strsql)
    End Sub
End Class

A classe clienteBLL implementa a interface IClienteBLL e dessa forma é obrigada a definir os métodos GetCliente() e IncluirCliente().

Vamos agora criar a classe que será a nossa factory chamada FactoryCliente com o seguinte código:

Imports BLL
Public Class FactoryCliente

   
Public Shared Function _clienteBLL() As IClienteBLL
        Return New clienteBLL
    End Function

End Class

A estrutura da nossa solução ficou da seguinte forma:

Agora podemos alterar o código da camada de interface removendo a criação da instância da classe ClienteBLL conforme abaixo:

Imports BLL

Public Class Form1

  
 Dim cliBLL As IClienteBLL = FactoryCliente._clienteBLL
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        DataGridView1.DataSource = cliBLL.GetCliente()
    End Sub

    Private Sub btnIncluir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnIncluir.Click
        Dim strSql As String = "Insert into Clientes Values ( '" & txtNome.Text & "','" & txtFone.Text & "')"
        cliBLL.IncluirCliente(strSql)
    End Sub
End Class

Vemos no código que não dependemos mais da implementação da classe ClienteBLL; a responsabilidade disso passou para a camada de negócios.

Assim desacoplamos a camada de interface da camada de negócios alcançando nosso objetivo de ter um baixo acoplamento nesta camada.

Este é um exemplo bem simples que adota um cenário ingênuo para que o conceito básico possa ser entendido por quem esta iniciando a trabalhar no mundo orientado a objetos.

Vimos assim um exemplo prático da utilização das interfaces e de um padrão de projeto, o padrão Factory.

Pegue o projeto completo aqui: BaixoAcoplamento.zip

Efésios 1:7-9"em quem temos a redenção pelo seu sangue, a redenção dos nossos delitos, segundo as riquezas da sua graça, que ele fez abundar para conosco em toda a sabedoria e prudência, fazendo-nos conhecer o mistério da sua vontade, segundo o seu beneplácito, que nele propôs..."

Referências:


José Carlos Macoratti