Null Object Pattern
- Type : Comportemental
- Difficulté : 3/10
- Définition succincte : Le Null Object Pattern est un design pattern comportemental qui propose de remplacer l'absence d'un objet par un objet "neutre" ou "null", au lieu d'utiliser une valeur
null. Cet objet neutre implémente la même interface que les autres objets, mais ne fait rien ou retourne des valeurs par défaut. Cela permet d'éviter les vérifications explicites denulldans le code et rend le système plus robuste et facile à maintenir.
L'objectif du Null Object Pattern est de simplifier le code en évitant les vérifications explicites de null. Au lieu de renvoyer null, on renvoie un Null Object qui implémente la même interface que l'objet attendu mais n'a aucun effet ou retourne des valeurs par défaut. Cela permet d'améliorer la lisibilité du code et de prévenir les erreurs potentielles liées aux objets inexistants.
Structure du Null Object Pattern
- AbstractObject : Interface ou classe abstraite qui définit le comportement attendu d'un objet.
- RealObject : Implémentation concrète qui représente un objet réel avec des fonctionnalités effectives.
- NullObject : Une implémentation spéciale qui remplace l'absence d'un objet et ne fait rien ou retourne des valeurs par défaut.
Implémentation avec Laravel
Imaginons que tu développes une application où tu dois gérer les utilisateurs. Dans certains cas, tu pourrais ne pas trouver un utilisateur correspondant à un identifiant donné. Au lieu de vérifier constamment si l'utilisateur est null, tu peux utiliser le Null Object Pattern pour retourner un NullUser lorsque l'utilisateur n'est pas trouvé, et ainsi éviter des erreurs comme des null reference exceptions.
a) Créer une interface UserInterface (AbstractObject)
L'interface UserInterface définit les méthodes que les objets User et NullUser doivent implémenter.
// app/Contracts/UserInterface.php
namespace App\Contracts;
interface UserInterface
{
public function getName(): string;
public function getEmail(): string;
public function isNull(): bool;
}
b) Implémenter la classe User (RealObject)
Cette classe représente un utilisateur réel récupéré de la base de données.
// app/Models/User.php
namespace App\Models;
use App\Contracts\UserInterface;
class User implements UserInterface
{
protected $name;
protected $email;
public function __construct($name, $email)
{
$this->name = $name;
$this->email = $email;
}
public function getName(): string
{
return $this->name;
}
public function getEmail(): string
{
return $this->email;
}
public function isNull(): bool
{
return false; // Ce n'est pas un objet null
}
}
c) Implémenter la classe NullUser (NullObject)
La classe NullUser représente un utilisateur neutre, avec des valeurs par défaut, qui est utilisé lorsque l'utilisateur réel n'existe pas.
// app/Models/NullUser.php
namespace App\Models;
use App\Contracts\UserInterface;
class NullUser implements UserInterface
{
public function getName(): string
{
return 'Guest'; // Nom par défaut pour un utilisateur null
}
public function getEmail(): string
{
return 'guest@example.com'; // Email par défaut
}
public function isNull(): bool
{
return true; // Ceci est bien un objet null
}
}
d) Créer un repository pour gérer les utilisateurs
Le repository va renvoyer un utilisateur réel si trouvé, ou un NullUser si l'utilisateur n'existe pas.
// app/Repositories/UserRepository.php
namespace App\Repositories;
use App\Models\User;
use App\Models\NullUser;
use App\Contracts\UserInterface;
class UserRepository
{
public function findById(int $id): UserInterface
{
// Simuler la récupération d'un utilisateur depuis la base de données
if ($id === 1) {
return new User('John Doe', 'john@example.com');
}
// Retourner un NullUser si l'utilisateur n'existe pas
return new NullUser();
}
}
e) Utilisation du Null Object Pattern dans une commande CLI
Nous allons maintenant créer une commande pour rechercher des utilisateurs et afficher leurs informations, sans avoir besoin de vérifier si l'utilisateur est null.
// app/Console/Commands/FindUser.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Repositories\UserRepository;
class FindUser extends Command
{
protected $signature = 'user:find {id}';
protected $description = 'Trouver un utilisateur par ID';
protected $userRepository;
public function __construct(UserRepository $userRepository)
{
parent::__construct();
$this->userRepository = $userRepository;
}
public function handle()
{
// Récupérer l'utilisateur par ID
$user = $this->userRepository->findById($this->argument('id'));
// Afficher les informations de l'utilisateur
$this->info("User Name: " . $user->getName());
$this->info("User Email: " . $user->getEmail());
// Afficher si c'est un utilisateur null
if ($user->isNull()) {
$this->warn("This is a guest user.");
}
}
}
f) Exécution de la commande
Tu peux exécuter cette commande pour rechercher un utilisateur avec l'ID donné. Si l'utilisateur n'existe pas, un NullUser sera renvoyé à la place.
php artisan user:find 1
Résultat attendu (si l'utilisateur existe) :
User Name: John Doe
User Email: john@example.com
Si l'utilisateur n'existe pas :
php artisan user:find 2
Résultat attendu :
User Name: Guest
User Email: guest@example.com
This is a guest user.
Avantages du Null Object Pattern
- Simplicité et lisibilité : Il élimine les vérifications explicites de
nulldans le code, rendant la logique plus simple et plus lisible. - Prévention des erreurs : En remplaçant
nullpar un objet neutre, tu réduis les risques de rencontrer des erreurs de référencenull. - Comportement uniforme : Le Null Object se comporte comme un objet normal, ce qui permet de le manipuler comme tel sans avoir à vérifier son existence.
Inconvénients du Null Object Pattern
- Complexité inutile : Dans des cas simples où la gestion de
nullest triviale, ce pattern peut introduire une complexité supplémentaire. - Difficulté à repérer les erreurs : L'usage excessif de ce pattern peut masquer des erreurs où l'absence d'un objet devrait être explicitement signalée, plutôt que traitée silencieusement.
Conclusion
Le Null Object Pattern est une technique utile pour gérer les situations où l'absence d'un objet peut survenir fréquemment. En encapsulant cette absence dans un objet neutre, tu évites les vérifications répétitives de null et améliores la lisibilité du code. Bien que ce pattern ne soit pas utilisé nativement dans Laravel, il peut être avantageux dans des scénarios où la gestion des objets manquants doit être simplifiée.