VB .NET - O padrão Factory revisitado
Este artigo vai tratar do conceito que envolve padrão de projetos, especificamente o padrão de projeto Factory. (eu já tratei desse assunto no artigo: Design Patterns - o padrão Factory )
Se você é novato na programação orientada a objetos pode ficar muito confuso ao tentar entender os conceitos sobre padrão de projetos e a descrição de um padrão como o padrão Factory.
Pensando nisso, resolvi dar uma pequena introdução aos conceitos da programação orientada a objetos (daqui pra frente chamarei de POO) para que o entendimento seja facilitado.
Um introdução bem básica ao conceito da POO para os inicantes.
A base do conceito da POO são classes e objetos.
Nota: A literatura cita que os pilares da POO são: Abstração, Encapsulamento, Herança e Polimorfismo.
E este conceito base é muito importante e pode confundir um iniciante. Afinal qual a diferença entre classes e objetos ?
Pense em uma classe como um modelo que é usado para criar algo.
Como exemplo pense em um bolo de banana. Para fazer um bolo de banana precisamos de uma receita de bolo de banana.
A receita é a classe e o bolo o objeto.
==> | ||
Classe | Objeto |
A classe é apenas usada para criar os objetos baseados nela.
São os objetos que realmente são usados na POO. Afinal ninguém vai comer uma receita de bolo de banana mas o bolo de banana que a receita criou.
Na POO é comum dizer que um objeto é a instância de uma classe. (guarde bem este conceito pense no bolo criado pela receita.)
Na linguagem Visual Basic .NET e C# (e qualquer outra linguagem orientada a objetos) tudo são objetos.(não vou polemizar...)
Para criar uma nova classe na linguagem VB .NET usamos as palavra chave Class/End Class. Abaixo temos a criação da classe Pessoa no VB .NET:
Obs: por convenção o nome de uma classe começa com caixa alta.
Public
Class Pessoa ----
Variáveis End Class |
Obs: Uma classe pode conter variáveis, métodos, propriedades e eventos.
Para criar um objeto da classe devemos criar uma instância da classe e isso é feito na linguagem VB .NET usando a palavra-chave new.
Assim para criar o objeto macoratti como instância da classe Pessoa fazemos assim:
Dim macoratti as Pessoa = new Pessoa() | ou assim | Dim
macoratti as Pessoa macoratti = new Pessoa() |
A classe Pessoa vai ser usada através do objeto macoratti instanciado a partir da classe.
Resumindo:
1- Objeto
- Um objeto é a representação de
um modelo abstrato definido em uma classe;
- Um objeto é uma instância de uma classe;
- O estado de um objeto é o conjunto de suas propriedades;
- Cada objeto tem o seu próprio estado;
- O comportamento de um objeto são as funções (métodos) que
afetam suas propriedades e/ou as propriedades de outros objetos;
2- Classe
- Uma classe é o modelo a partir do
qual os objetos serão criados;
- Um objeto é uma instância de uma classe;
- Uma classe define as propriedades e comportamento que cada uma
de suas instâncias(objetos) possuirá;
- Classes podem se comunicar em entre si trocando informações(dados,
parâmetros, controles,etc.);
O padrão Factory
Feita a introdução voltemos ao assunto principal do artigo : o padrão Factory.
Para não ficar me repetindo vou citar o conceito adaptado da wikipédia.
Um padrão de projeto simplesmente descreve uma solução para um problema recorrente no desenvolvimento de sistemas de software orientados a objetos. Um padrão de projeto estabelece um nome, define o problema, a solução, quando aplicar esta solução e suas conseqüências.(adpatado da wikipédia).
O objetivo em usar um padrão de projeto é : Não reinventar a roda |
Para definir o padrão Factory vou me repetir:
O padrão Factory fornece uma interface para a criação de famílias de objetos correlatos ou dependentes sem a necessidade de especificar a classe concreta destes objetos.
Por exemplo , quando estamos trabalhando com uma interface e temos mais de uma implementação para esta interface, podemos utilizar uma fábrica para criar um objeto que implementa a interface; a fábrica pode selecionar a implementação que ela retorna.
A utilização do padrão Factory é útil quando você precisa criar objetos dinamicamente sem conhecer a classe de implementação, somente sua interface: o padrão factory estabelece uma forma de desenvolver objetos que são responsáveis pela criação de outros objetos.
Para melhor entender a utilização do padrão e sua atuação vou usar um exemplo a criação de uma fábrica de empregados usando a linguagem VB .NET.
Vamos imaginar uma aplicação que possui uma interface para aceitar a seleção por parte do usuário do tipo de funcionário e que partir da seleção crie novos objetos funcionario. A aplicação lida com 3 tipos de funcionários representados pelas seguintes classes:
Cada uma das classes acima herda da classe Funcionario que possui propriedades e métodos comuns a todos os funcionários, como nome, matricula, setor, etc.
A seguir temos o código de cada uma das classes usadas pela aplicação na linguagem VB .NET:
Funcionario | FunciAssalariado | FunciHorista | FunciEstagiario |
Public Class Funcionario Private _nome As String Private _matricula As String Private _setor As String Private _tipo As String Property Nome As String Get Return _nome End Get Set(ByVal value As String) _nome = value End Set End Property Property Matricula As String Get Return _matricula End Get Set(ByVal value As String) _matricula = value End Set End Property Property Setor As String Get Return _setor End Get Set(ByVal value As String) _setor = value End Set End Property Property Tipo As String Get Return _tipo End Get Set(ByVal value As String) _tipo = value End Set End Property End Class |
Public Class FunciAssalariado Inherits Funcionario Private _valorSalario As Double Property Salario As Double Get Return _valorSalario End Get Set(ByVal value As Double) _valorSalario = value End Set End Property End Class |
Public Class FunciHorista Inherits Funcionario Private _valorHora As Double Property ValorHora As Double Get Return _valorHora End Get Set(ByVal value As Double) _valorHora = value End Set End Property End Class |
Public Class FunciEstagiario Inherits Funcionario Private _valorEstagio Property ValorEstagio As Double Get Return _valorEstagio End Get Set(ByVal value As Double) _valorEstagio = value End Set End Property End Class |
Observe que todas as classes herdam da classe Funcionario. A herança no VB .NET é implementada usando a palavra-chave inherits.
O formulário da aplicação é bem simples e aceita a informação do nome, matrícula, setor e do tipo de funcionário.
Ao clicar no botão Criar a aplicação executa o seguinte código:
Private Sub btnCriarFuncionario_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCriarFuncionario.Click Dim novoFuncionario As Funcionario = Nothing If cboTipoFuncionario.SelectedItem.ToString = "Assalariado" Then novoFuncionario = New FunciAssalariado ElseIf cboTipoFuncionario.SelectedItem.ToString = "Horista" Then novoFuncionario = New FunciHorista ElseIf cboTipoFuncionario.SelectedItem.ToString = "Estagiário" Then novoFuncionario = New FunciEstagiario End If novoFuncionario.Nome = txtMatricula.Text novoFuncionario.Matricula = txtMatricula.Text novoFuncionario.Setor = txtSetor.Text novoFuncionario.Tipo = cboTipoFuncionario.SelectedItem.ToString End Sub |
O código acima é muito simples mas esconde alguns problemas. Vejamos:
O código esta fortemente acoplado com a classe Funcionario.
Mas o que vem a ser esse negócio de fortemente acoplado ? Isso é bom ou ruim ?
Para esclarecer vejamos rapidamente o que significa isso:
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;
Para ilustrar imagine que um novo tipo de funcionário deverá ser tratado pela aplicação : o funcionário Contratado (classe FunciContratado) que tem a propriedade valor do contrato: ValorContrato.
Quais as implicações dessa alteração no código:
As duas primeira alterações são inevitáveis e são uma consequência da mudança na regra de negócio da aplicação mas a última alteração pode trazer implicações que poderão afetar toda a minha aplicação pois existe um forte acoplamento do código com a classe Funcionario.
Não seria maravilhoso se fazer essa alteração não precisássemos realizar nenhuma alteração no código do formulário a não ser a recompilação das classes ?
Mas como seria isso possível ?
Que tal se movermos o código que cria as instâncias das classes do formulário para a classe Funcionario ?
Com isso estaríamos minimizando o impacto causando pela alteração e tornando o código menos acoplado a classe Funcionario.
Aqui entra o padrão Factory.
Vamos incluir o seguinte código na classe Funcionario:
Public Shared Function CriarFuncionario(ByVal funciTipo As String) As Funcionario Dim novoFuncionario As Funcionario = Nothing If funciTipo = "Assalariado" Then novoFuncionario = New FunciAssalariado ElseIf funciTipo = "Horista" Then novoFuncionario = New FunciHorista ElseIf funciTipo = "Estagiário" Then novoFuncionario = New FunciEstagiario ElseIf funciTipo = "Contratado" Then novoFuncionario = New FunciContratado End If Return novoFuncionario End Function |
Este método estático chamado CriarFuncionario() recebe o tipo de funcionário e cria uma instância da respectiva classe do funcionário.
As propriedades e métodos de uma classe podem ser membros de instância da classe ou membros compartilhados(shared) ou estáticos. Membros de instância são associados com instâncias de um tipo , enquanto que membros estáticos (shared) são associados com a classe e não com uma instância em particular. Métodos
são métodos de instância a menos que você
explicitamente os defina como métodos estáticos (shared)
usando a palavra-chave Shared. |
O código do formulário ficaria da seguinte forma:
Private Sub btnCriarFuncionario_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCriarFuncionario.Click Dim novoFuncionario As Funcionario novoFuncionario = Funcionario.CriarFuncionario(cboTipoFuncionario.SelectedItem.ToString) novoFuncionario.Nome = txtMatricula.Text novoFuncionario.Matricula = txtMatricula.Text novoFuncionario.Setor = txtSetor.Text novoFuncionario.Tipo = cboTipoFuncionario.SelectedItem.ToString End Sub |
Observe que não temos mais a palavra-chave new sendo usada para criar instâncias das classes de funcionários, todo o código responsável pela criação das instâncias foi movido para a classe Funcionario que é quem deve saber como fazer isso. (Veja o princípio da responsabilidade única - SRP)
O que fizemos foi implementar o padrão Factory para resolver o nosso problema. A nossa implementação retorna uma instância de uma das diversas classes dependendo do parâmetro passado para o método estático (Shared) CriarFuncionario() que é o único responsável por retornar a instância da classe apropriada.
Obs: Poderíamos criar outra classe chamada FuncionarioFactory() para realizar o mesmo trabalho.
Se você desejar proteger a classe Funcionario de forma que a mesma não seja instanciada diretamente pela aplicação basta definir um construtor protegido da seguinte forma:
Protected
Sub New() End Class |
Fazendo isso você não permite que ninguém que esteja fora da sua classe crie uma instância da mesma. Se alguém tentar criar uma instância de Funcionario no formulário obterá uma mensagem de erro conforme a figura abaixo:
Dessa forma você 'força' a utilização da sua classe para criar instâncias das classes de funcionários.
Com isso mostrei como usar o padrão Factory e qual o conceito básico associado a ele.
Pegue o código completo usado no artigo aqui: padraoFactory.zip
Eu sei é apenas VB .NET, mas eu gosto...
Referências: