Poltergeist (programmation informatique).
Objet éphémère inapproprié
En programmation informatique, un poltergeist (ou roulotte de gitan) est un objet éphémère, généralement sans état, utilisé pour effectuer une initialisation ou pour invoquer des méthodes dans une autre classe plus permanente. Il est considéré comme un anti-pattern. La définition originale est due à Michael Akroyd, lors de la conférence Object World West de 1996 :
De même qu'une roulotte de gitan ou un poltergeist apparaît et disparaît mystérieusement, il en va de même de cet objet à courte durée de vie. En conséquence, le code est plus difficile à maintenir et l'on gaspille inutilement des ressources. La cause habituelle de cet anti-pattern est une mauvaise conception des objets.
Un poltergeist peut souvent être identifié par son nom ; ces noms contiennent fréquemment des mots tels que « Manager », « Controller », « Supervisor », « StartProcess », etc.
Parfois, des classes poltergeist sont créées parce que le programmeur a anticipé le besoin d'une architecture plus complexe. Par exemple, un poltergeist apparaît si une même méthode joue à la fois le rôle de client et d'invocateur dans un patron de commande, et que le programmeur anticipe la séparation des deux phases. Toutefois, cette architecture plus complexe peut en réalité ne jamais voir le jour.
Les poltergeists ne doivent pas être confondus avec les objets à longue durée de vie et porteurs d'état d'un patron tel que modèle–vue–contrôleur, ni avec les patrons de séparation en couches tels que le patron business delegate.
Pour supprimer un poltergeist, supprimez la classe et insérez ses fonctionnalités dans la classe invoquée, éventuellement par héritage ou sous forme de mixin.
Des méthodes ont été proposées pour détecter les poltergeists dans le code en vue d'une refactorisation.
Exemple
La classe Poltergeist de cet exemple en C++ peut être considérée comme un « objet poltergeist », car elle n'ajoute ni fonctionnalité ni encapsulation supplémentaire et ne fait qu'accroître la complexité par une abstraction inutile.
import std;
using String = std::string;
// Classe Poltergeist qui ne fait que détenir un pointeur, sans ajouter de comportement utile
class Poltergeist {
private:
String* s; // pointeur vers une chaîne, mais la classe elle-même ne fait rien d'utile
public:
explicit Poltergeist(String* s):
s{s} {}
~Poltergeist() {
delete s;
}
[[nodiscard]]
String get() const noexcept {
return s;
}
// Aucun comportement ni fonctionnalité utile supplémentaire
};
int main() {
// Crée un objet Poltergeist qui ne fait que détenir un pointeur vers la chaîne
Poltergeist p(new String("Hello, world!"));
// Se contente de faire circuler les données sans apporter de valeur
std::println(*p.get());
return 0;
}
On pourrait plutôt procéder de manière plus appropriée en utilisant un pointeur intelligent.
import std;
using String = std::string;
template <typename T>
using UniquePtr = std::unique_ptr<T>;
// Utiliser directement des pointeurs intelligents pour gérer la mémoire
UniquePtr<String> s = std::make_unique<String>("Hello, World!");
std::println(*s);
Un autre exemple d'objet poltergeist/roulotte de gitan est le suivant, où UserCreator est instancié uniquement pour effectuer quelques actions élémentaires.
import std;
using String = std::string;
class UserManager {
public:
void createUser(const String& name) {
std::println("User created: {}", name);
}
};
// La classe poltergeist
class UserCreator {
public:
explicit UserCreator(const String& name) {
UserManager manager;
manager.createUser(name);
}
};
int main() {
// Création d'un poltergeist uniquement pour appeler createUser()
UserCreator("Alice");
UserCreator("Bob");
}
On pourrait procéder de manière plus appropriée comme suit, en évitant entièrement toute classe poltergeist :
// Éviter entièrement le poltergeist UserCreator
int main() {
UserManager manager;
manager.createUser("Alice");
manager.createUser("Bob");
}