lunedì 17 settembre 2012

Come evitare NullReferenceException (Next Level)

Nel precedente post, abbiamo descritto eventuali strategie per evitare null reference exception. L'ultima scelta è ricaduta sulla seguente forma:

   repository.UseStudentFoundByCode(1234,new Output());


Ovvero passiamo al metodo che recupera le informazioni dello studente, un oggetto con la responsabilità di notificare i dati dello studente in un qualche formato. Vediamo come è stato implementato UseStudentFoundByCode:

   public void UseStudentFoundByCode(int code,
                                     IStudentSubstitute substitute)
   {
      var student = studentsDatabase.GetByCode(code);
      if (student == null)
         return;
      substitute.Perform(student);
   }

Ora vorrei illustrare una scelta leggermente più elegante per rendere la sintassi un po' più zuccherina:

   repository
      .StudentFoundByCode(1234)
      .Perform(new Output().Write);

Modifichiamo la classe Repository come segue:

   public class Repository
   {
      private StudentsDatabase studentsDatabase;
      public IStudentSubstitute StudentFoundByCode(int code)
      {
         var student = studentsDatabase.GetByCode(code);
         if (student == null)
            return new NullStudentSubstitute();
         return new StudentSubstitute(student);
      }
   }

La scelta ricade sul restituire un oggetto che può o non può eseguire operazioni sull'oggetto Student, quindi se GetByCode restituisce un'istanza valida, StudentFoundByCode deciderà a chi delegare le operazioni sull'oggetto Student.

Creiamo l'interfaccia comune:

   public interface IStudentSubstitute
   {
      void Perform(Action<Student> student);
   }

Implementiamo le due strategie per eseguire operazioni sull'oggetto Student:

   public class StudentSubstitute IStudentSubstitute
   {
      private readonly Student student;

      public StudentSubstitute(Student student)
      {
          this.student = student;
      }

      public void Perform(Action<Student> action)
      {
          action(student);
      }
   }

   public class NullStudentSubstitute IStudentSubstitute
   {
      public void Perform(Action<Student> action)
      {
      }
   }

Con questa soluzione centraliziamo le azioni che vogliamo svolgere sull'oggetto studente, ignari se l'oggetto student è stato creato.