Null object

La programmation avec design patterns

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
Dans la plupart des langages de programmation majeurs, les références inexistantes sont des null. Cela peut poser problème à la bonne exécution du programme soit en lançant une exception, soit en introduisant le comportement non-voulu. La solution à ce mal de tête se trouve dans null object pattern.

Le design pattern appelé null object permet de mieux contrôler le comportement des références qui ne pointent vers aucun objet. Il s'agit des références qui sont null et donc qui peuvent provoquer des exceptions. Pour en savoir plus, vous pouvez consulter l'article consacré à NullPointerException en Java. Ces exceptions apparaissent si l'on essaie à appeler une des méthodes de null ou d'accéder à une de ses propriétés.

Une des méthodes qui permettent d'éviter ce genre de problèmes est l'utilisation de l'instruction if-else. Cependant, le poids du code grandit rapidement si l'on effectue la vérification avant chaque opération. Une autre option est le bloc try-catch, mais lui aussi nuit à la lisibilité du code. Null object peut être dans certains cas une solution plus propre que la vérification éternelle.

Ce patron de conception est une sorte de fantôme qui reprend juste la forme d'une personne mais qui, malgré la présence de tous les composants (coeur, poumon etc.), est incapable de les utiliser. Si l'on traduit cela en langage de développement, on peut dire qu'on a une instance de la classe Person et un autre de la classe Ghost. Les deux implémente une interface Somebody. Imaginons maintenant une classe client, Mother, qui va demander à son fils de rentrer pour le déjeuner. Mother prend le téléphone et appelle, par exemple via la méthode callSon(). La méthode vérifiera si le fils peut décrocher. Si oui, elle retournera sa voix. Sinon, elle retournera la voix du fantôme. Voici le bout de code qui l'illustre :

public Somebody callSon(Mother mother) {
if (mother.hasSonAvailable()) return new Person(mother);
return Ghost();
}

Dans ce cas si l'instance du Mother effectue une opération sur l'implémentation du Somebody, elle ne provoquera l'exception. Même si l'on retourne l'instance du Ghost. Elle implémente la même interface que Son.

Null object design pattern est souvent utilisé avec un autre patron de conception, Singleton. De toute façon, l'objet null ne fait rien à part implémenter les méthodes vides. Ses multiples instances seront donc identiques et cela n'a pas grand intérêt d'en avoir plusieurs. Regardons ceci sur un exemple.

Exemple null object design pattern
Dans notre exemple un objet représentant des jumelles va essayer de distinguer si une silhouette correspond à celle d'une femme ou d'un homme. Cependant, il y aura des situations où la silhouette sera non-détectable (par exemple à cause d'un brouillard). Regardons d'abord l'interface qui représentera les objets participants (homme et femme) ainsi que les objets en eux-mêmes.

interface Person{
public void breathe();

public void eat();
}

class Man implements Person{
@Override
public void breathe(){
System.out.println("Oh");
}

@Override
public void eat(){
System.out.println("Mlask-mlask");
}
}

class Woman implements Person {
@Override
public void breathe() {
System.out.println("Ah");
}

@Override
public void eat() {
System.out.println("...");
}
}

Les classes n'ont rien de mystérieux. Elles représentent l'interface Person et implémentent les deux méthodes. La première permet de représenter la voix de la personne quand elle respire (breathe()). L'autre représente le son exprimé quand la personne mange. Regardons maintenant le null object, en occurrence illustrant un ombre :


class Shadow implements Person{
@Override
public void breathe(){
}

@Override
public void eat(){
}
}

On voit qu'il implémente les deux méthodes de l'interface. Cependant, contrairement aux Man et Woman, ces méthodes ne font rien. Et c'est la première condition à respecter en utilisant ce patron de conception. Le null object doit implémenter les méthodes définies dans l'interface. Cependant, ces méthodes ne peuvent rien faire. Regardons maintenant comment cela peut fonctionner ensemble :

public class NullObjectTest{
public static void main(String[] args){
Person foundPerson=Binoculars.lookForPerson(false);
foundPerson.breathe();
foundPerson.eat();
Person notFoundPerson=Binoculars.lookForPerson(true);
notFoundPerson.breathe();
notFoundPerson.eat();
}
}

class Binoculars{
public static Person lookForPerson(boolean isFog){
if(!isFog) return new Man();
return new Shadow();
}
}

On peut observer qu'à la place de retourner null et de vérifier si la personne a été reconnue dans main(), on retourne directement null object et invoque ses deux méthodes vides.

Null object peut être une réponse à plusieurs endroits dans le code où l'on peut effectuer une opération sans forcément vérifier la nullité de la référence. S'il s'agit d'une simple écriture des données dans les fichiers, l'utilisation du null object peut être utile. Par contre, il ne faut pas abuser avec son utilisation. Dans les applications il y aura toujours les endroits où l'on voudra vérifier si un objet est correct, par exemple, avant l'insertion d'une information dans la base de données. On ne va pas alors insérer une ligne vide, mais on effectuera plutôt la validation if != null avant d'effectuer l'opération.
Bartosz KONIECZNY 16-06-2014 06:32 design patterns
Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil PHP

Comment créer un base string pour OAuth

Création d'un base string pour OAuth consiste à récupérer 3 éléments et les concaténer. Il s'agit de : - la méthode HTTP d'appel de la ressource (GET ou POST) - l'url de l'appel (sans paramètres) - les paramtères utilisés dans la requête (il ne faut pas inclure oauth_signature car elle n'est pas encore générée) Tous les 3 éléments doivent être normalisés et concaténés avec un ampersand (&). L'ampersand ne peut pas subir l'encodage. Une méthode pour générer un base string peut se présenter ainsi :

function setBaseString($method, $url, $paramsList)
{
  $paramsString = array();
  foreach($paramsList as $p => $param)
  {
    $paramsString[] = $p.'='.$param;
  }
  return str_replace('%7E', '~', rawurlencode($method)).'&'.str_replace('%7E', '~', rawurlencode($url)).'&'.str_replace('%7E', '~', rawurlencode(implode('&', $paramsString)));
}