.NET - Padrão de Projeto - Null Object Pattern


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 de 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

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 de retornam valores fixos e definidos. Isso permite que 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:

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)

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

A seguir vamos definir no método getRequisicao o tratamento do Null pela classe NullRequisicao.

Agora ao invés de retornar null o método irá retornar um objeto do tipo NullRequisicao que implementará a interface IRequisicao e retornando um valor padrão:

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 :

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

Implementação C#

1- 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();
   }
}

2- Inteface IRequisicao

public interface IRequisicao
{
void executar();
}

3- A classe NullRequisicao

class NullRequisicao : IRequisicao
{
  public void executar()
  {
Console.WriteLine("executou o comando null");
  }
}

4- 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

Mat 7:9 Ou qual dentre vós é o homem que, se seu filho lhe pedir pão, lhe dará uma pedra?

Mat 7:10 Ou, se lhe pedir peixe, lhe dará uma serpente?

Mat 7:11 Se vós, pois, sendo maus, sabeis dar boas dádivas a vossos filhos, quanto mais vosso Pai, que está nos céus, dará boas coisas aos que lhas pedirem?

Mat 7:12 Portanto, tudo o que vós quereis que os homens vos façam, fazei-lho também vós a eles; porque esta é a lei e os profetas.

Referências:


José Carlos Macoratti