.NET

13 fev, 2014

.NET – Padrão de Projeto: Null Object Pattern

Publicidade

Todos concordam que a reutilização de código é uma importante referência que todo o desenvolvedor deve buscar a fim de tornar o seu trabalho robusto e rentável. Afinal, ninguém quer ficar duplicando código existente para realizar a mesma tarefa repetidas vezes, pois além de ser trabalhoso é uma fonte de erros.

Com isso em mente, eu vou entrar no assunto do nosso artigo. Você já pensou quantas vezes você verifica se um objeto é null em seu código?

O problema do Null/Nothing

Mas o que significa Null (ou Nothing para o VB .NET)? No Visual Basic, a palavra-chave Null/Nothing indica um valor Null.

Null ou Nothing pode ser visto como um valor que indica dados ausentes ou desconhecidos em um campo (alguns campos, como aqueles definidos como contendo a chave primária, não podem conter valores Null).

Você pode usar valores Null em expressões e inserir valores null em campos dos quais as informações são desconhecidas. Null é diferente de zero(0); é diferente de uma string vazia (“”), e,  é diferente de uma variável que não foi inicializada (empty). Null/Nothing é alguma coisa indefinida…

Muitas tabelas de banco de dados possuem campos que não contêm valor. Um campo que não contém valor pode possuir um Null (Nulo) ou uma sequência vazia (“”).

Um valor Null pode indicar que a informação existe mas é desconhecida, ou seja , se um campo “Telefone” não contém valores, pode indicar que ou o cliente não possui telefone ou que o seu telefone não é conhecido.

Geralmente usa-se Null para indicar que o campo não contém valores conhecidos. Usamos uma sequência vazia para indicar que, no caso do campo “Telefone”,  o cliente não possui telefone – para inserir uma sequência vazia atribua aspas duplas sem espaços ao campo(“”).

Objetos também podem possuir o valor Null/Nothing indicando que não existe uma referência ao objeto.

Tratando o Null/Nothing

Mais comumente, uma exceção NullReferenceException é lançada por um método que é passado Null ou Nothing. Alguns métodos validam os argumentos recebidos e ao fazerem isso, se um dos argumentos for Null ou Nothing, o método lança uma exceção System.ArgumentNullException. Caso contrário, ele lança uma exceção NullReferenceException. O exemplo a seguir ilustra esse cenário.

Module Module1

    Public Sub Main()
        Dim nomes As List(Of String) = GetDados()
        PopularNomes(nomes)
        Console.ReadKey()
    End Sub

    Private Sub PopularNomes(nomes As List(Of String))
        Dim arrNomes() As String = {"Macoratti", "Samuel", "Jefferson",
                                                 "Miriam", "Jessica", "Bianca", "Yuri"}
        For Each arrNome In arrNomes
            nomes.Add(arrNome)
        Next
    End Sub

    Private Function GetDados() As List(Of String)
        Return Nothing
    End Function

End Module

net_null1

Diante deste cenário, podemos afirmar que tanto na linguagem C# como na VB .NET podemos ter referências a objetos com valores nulo ou null/Nothing. Sendo que estas referências devem ser verificadas para garantir que elas não possuam um valor nulo antes de chamar os métodos do objeto, caso contrário iremos obter uma exceção.

Aqui entra o padrão Objeto Null…

O padrão Object Null

O padrão de projeto Objeto Null simplifica o uso de dependências que podem não estar definidas, possuindo assim um valor null. Ao invés de referências nulas, o padrão utiliza uma classe concreta que implementa uma interface conhecida mas que retorna um valor vazio ao invés de null.

Em vez de usar uma referência nula para transmitir a ausência de um objeto ou de uma referência do objeto (por exemplo, um cliente inexistente), podemos usar um objeto que implementa uma interface esperada, mas cujo método está vazio. A vantagem deste método em relação à implementação padrão é que um objeto nulo é previsível e não tem efeitos colaterais, pois ele não faz nada.

Assim uma função pode recuperar uma lista de arquivos em uma pasta e executar alguma ação em cada arquivo. No caso de uma pasta vazia, uma resposta pode ser o lançamento de uma exceção ou o retorno de uma referência nula. Assim, o código deve verificar a lista retornada e ver se ela é nula.

Quando retornarmos um objeto nulo (ou seja, uma lista vazia), não há a necessidade de verificar se o valor de retorno é de fato uma lista. A função de chamada pode simplesmente iterar sobre a lista normalmente e não fazer nada. Assim, em vez de passar referências nulas e verificar a presença de null antes de qualquer operação, você pode criar uma classe específica que representa uma dependência não-funcional.

Esta classe implementa uma interface esperada ou herda de uma classe abstrata, mas não inclui nenhuma funcionalidade. Seus métodos e propriedades não executam nenhuma ação e retornam valores fixos e definidos. Isso permite a qualquer objeto dependente usar as dependências de objetos nulos sem a realização de qualquer pré-verificações, simplificando o código.

O padrão de objeto nulo é geralmente usado com outros padrões de projeto. A própria classe de objeto nulo é muitas vezes criada como um Singleton. Isso limita o número de instâncias para uma; o que é ideal, visto que objetos nulos geralmente não possuem nenhum estado e nenhuma funcionalidade de forma que a criação de objetos adicionais acrescentaria uma sobrecarga desnecessária ao seu projeto.

Outro padrão de design que é freqüentemente encontrado com o padrão de objeto nulo é o padrão Strategy. Quando uma das estratégias não requer funcionalidade, um objeto nulo pode ser usado. Um terceiro padrão associado ao padrão object null é o padrão Factory Method, onde a fábrica pode retornar uma instância de objeto nulo.

Implementando o padrão de projeto Object Null

Abaixo vemos o diagrama de classes UML que descreve uma implementação do padrão de objeto nulo. Os itens do diagrama são descritos a seguir:

net_null2

O padrão Object Null é um objeto que encapsula a ausência de um objeto. Ele fornece o comportamento para não fazer nada e retornar um valor padrão. Esse padrão de projeto é usado sempre que a referência a um objeto pode ser nula. O uso do padrão Object Null simplifica o código e o torna menos propenso a erros. Este padrão permite a criação de um objeto que é utilizado para substituir a lógica de verificação de nulos.

  • Client – Esta classe tem uma dependência que pode ou não ser necessária. Quando nenhuma funcionalidade for necessária na dependência, ele irá executar os métodos de um objeto nulo.
  • DependencyBase – Essa classe abstrata é a classe base para as várias dependências disponíveis que o cliente pode usar. É também a classe base para a classe objeto nulo. Onde a classe de base não fornece nenhuma funcionalidade partilhada, ela pode ser substituída por uma interface.
  • Dependency – Esta classe é uma dependência funcional que pode ser usada pelo cliente.
  • NullObject – Esta é a classe objeto nulo que pode ser utilizada como uma dependência pelo cliente. Ele não contém nenhuma funcionalidade, mas implementa todos os membros definidos pela classe abstrata/interface DependencyBase.

Implementação VB .NET

Imagine que temos uma classe chamada Comandos que contenha um método chamado getRequisicao() que recebe uma string e executa um comando para uma determinada requisição.

Public Class Comandos

    Private Function getRequisicao(comando As String) As IRequisicao
        If comando.Equals("A") Then
            Return New RequisicaoA()
        End If
        If comando.Equals("B") Then
            Return New RequisicaoB()
        End If
    End Function

End Class

Para usar esta classe teremos que verificar se a referência retornada pelo método getRequisicao não é um null. Veja o código abaixo:

Module Module1
    Sub Main()

        Dim comando As String = "A"
        Dim req As New Comandos
        Dim _requisicao = req.getRequisicao(comando)

        If IsNothing(_requisicao) Then
            Console.WriteLine("Apos verificar se a referência não é nula executa a ação")
        else
          _requisicao.executar()
        End If

    End Sub
End Module

Vamos agora definir uma interface IRequisicao contendo um método executar:

Interface IRequisicao
    Sub executar()
End Interface

Agora ao invés de retornar null, o método irá retornar um objeto do tipo NullRequisicao, que implementará a interface IRequisicao e retorná um valor padrão. A seguir vamos definir no método getRequisicao o tratamento do Null pela classe NullRequisicao.

Abaixo o código da classe Comandos que trata o objeto Null pela classe NullRequisicao:

Public Class Comandos

    Private Function getRequisicao(comando As String) As IRequisicao
        If comando.Equals("A") Then
            Return New RequisicaoA()
        End If
        If comando.Equals("B") Then
            Return New RequisicaoB()
        End If
       Return New NullRequisicao()
    End Function

End Class

A seguir, vemos a classe NullRequisicao que implementa a interface IRequisicao():

Public Class NullRequisicao
          Implements IRequisicao

    Public Sub executar() Implements IRequisicao.executar
        aviso.alerta("executou o comando null")
    End Sub

End Class

Agora não precisamos mais verificar se a referência é null, pois teremos um retorno padrão.

Module Module1
    Sub Main()

        Dim comando As String = "A"
        Dim req As New Comandos
        Dim _requisicao = req.getRequisicao(comando)
        _requisicao.executar()
    End Sub
End Module

Diagrama de classes UML para a implementação do padrão Object Null:

net_null3

Abaixo temos os códigos para a implementação C#:

Implementação C#

  • Classe Comandos
public class Comandos
{
   private IRequisicao getRequisicao(string comando)
   {
if (comando.Equals("A")) {
          return new RequisicaoA();
}
if (comando.Equals("B")) {
return new RequisicaoB();
}
          return new NullRequisicao();
   }
}
  • Inteface IRequisicao
public interface IRequisicao
{
void executar();
}
  • A classe NullRequisicao
class NullRequisicao : IRequisicao
{
  public void executar()
  {
Console.WriteLine("executou o comando null");
  }
}
  • Exemplo de utilização
public static void Main()
 {             
  string comando = "C";
  Comandos req = new Comandos();
  _requisicao = req.getRequisicao(comando);

  if ((_requisicao == null)) {
      Console.WriteLine("retornou Null/Nothing");
     } else {
        _requisicao.executar();
     }
  Console.ReadKey();
 }

O padrão de objeto nulo nem sempre é necessário, às vezes você quer fazer alguma coisa ao se deparar com um nulo. O padrão é mais indicado quando um valor nulo tenha efeitos catastróficos no seu código, e onde um valor padrão adequado pode ser retornado ou uma simples ação padrão pode ser tomada.

Pegue o projeto completo aqui: PadraoObjectNull.zip