Facciamo un esempio:
var
student = repository.GetStudentByCode(1234);
output.Write(student);
Ora ipotiziamo che non ci sia nessun studente con codice 1234. In
questo caso GetStudentByCode restituisce null, quindi passiamo null
al metodo Write.
Vediamo come è implementato il metodo Write:
{
public void Write(Student student)
{
Console.WriteLine(string.Format("Studente {0} {1}",
student.Lastname,
student.Name));
}
}
Ovviamente, otterremo una bella null reference exception; per evitare l'eccezione potremmo verificare se student é diverso da null:
var
student = repository.GetStudentByCode(1234);
if
(student != null)
output.Write(student);
Questo introduce l'utilizzo di if, potremmo pensare di spostare
l'if dentro il metodo Write per cercare di ridurre il prolificare di
if prima di chiamare Write. Ma questo comporta la duplicazione del controllo quando creiamo un nuovo metodo per salvare su file i dati dello studente; quindi per ogni metodo che restituisce un valore
dovremmo verificare che il valore sia valido.
Anche intercettare l'eccezione, generata dall'utilizzo di una null reference, non risolvere il nostro problema. Il codice potrebbe avere la seguente forma:
var
student = repository.GetStudentByCode(1234);
try
{
output.Write(student);
}
catch
(Exception
ex )
{
//
do something
}
Le problematiche di gestione risultano essere identiche per l'uso dell' if o del try..catch.
Per eliminare la necessità di verificare se il valore restituito da GetStudentByCode è diverso da null, potremmo introdurre un NullObject di tipo Student e quindi modificare GetStudentByCode per restituire NullStudent se la ricerca con matricola 1234 non da un risultato valido.
Questa scelta di design elimina questo
if(student!=null) in tutti i punti dove viene utilizzato student.
Putroppo passando NullStudent al metodo Write avremmo un
comportamento inaspettato, ad esempio delle righe con solo la
scritta "Studente ".
Per ovviare a questo problema ed evitare di verificare in diverse
parti del nostro codice se student é diverso da null, cambiamo la
signature di GetStudentByCode e la definiamo come segue:
public
Student
GetStudentByCode(int
code)
diventa:
public
void
UseStudentFoundByCode(int
code,
IStudentSubstitute substitute)
IStudentSubstitute substitute)
{
var
student = _studentsDatabase.GetByCode(code);
if
(student == null)
return;
substitute.Perform(student);
}
{
public
void
Perform(Student
student)
{
Console.WriteLine(string.Format("Studente
{0}
{1}",
student.Lastname,
student.Name));
}
}
A questo punto il codice chiamante può essere modificato da:
var
student = repository.GetStudentByCode(1234);
if
(student != null)
output.Write(student);
diventa:
repository.UseStudentFoundByCode(1234,new
Output());
Come possiamo notare il codice si è ridotto e non c'è possibilità che gli if (per il controllo di Student uguale a null) si prolifichino.