Accueil / Tutoriels / Prototype

Prototype

Publié le 24 Mars 2025
Conception

Prototype est un design pattern de création qui permet de cloner des objets existants au lieu d’en créer de nouveaux à partir de zéro. C’est un pattern très utile surtout si la création de nouveaux objets est gourmand en ressources.

Principe

Nous allons déléguer la logique de clonage aux objets susceptibles d’être copiés en déclarant une interface commune à tous ces objets. Ainsi, on découple la logique de clonage d’un objet de la classe de l’objet lui-même.

Exemples d’utilisations

Création dynamique d’éléments pour une interface utilisateur

Dans une application web, on peut avoir des composants dynamiques qui doivent être dupliqués et personnalisés, comme des popups, des widgets ou des notifications. Au lieu de créer chaque élément à la main, on utilise un prototype pour cloner des composants rapidement. Plus besoin d’écrire la structure d’un élément à chaque fois. Chaque élément peut être adapté rapidement.

Charger une configuration

Dans une application web, on utilise souvent des fichiers de configuration pour stocker des paramètres (pour une base de données, des services tiers, etc…). Si plusieurs services ont besoin d’une configuration similaire mais avec de légères différences, on peut dans ce cas cloner un prototype plutôt que charger et refaire tout un fichier de configuration.

Points d’attentions

Selon la structure des objets, le clonage peut être simple ou complexe. Par exemple, un clonage simple ne duplique pas les objets imbriqués, tandis qu’un clonage complexe recrée totalement un objet et ses structures imbriqués, ce qui peut être coûteux en ressources et difficile à mettre en place. Dans certains langages comme Go ou PHP, un clonage complexe peut nécessiter des fonctions spécifiques ou des librairies.

Le clonage peut cacher la logique de création d’un objet rendant le code “moins lisible” par rapport à une instanciation classique avec un constructeur qui est plus compréhensible.

Certains objets peuvent être liés à des ressources externes (un connexion à une base de données par exemple) et ne sont pas clonables. En règle générale, le pattern Prototype ne fonctionne pas bien avec l’injection de dépendances, car il faudrait aussi cloner toutes les dépendances de l’objet concerné. Dans ce cas, mieux vaut utiliser un autre pattern comme Factory.

Implémenter un prototype

Le pattern Prototype consiste à fournir une méthode pour le clonage aux classes cibles. Plutôt que d’instancier un constructeur, on va copier un objet existant. Pour ce faire, on met définit une interface qui contient une méthode que l’on nomme en général clone() et chaque classe va implémenter cette méthode pour retourner une copie d’elle-même.

Voici un exemple en Go :


package email

import "fmt"

// Interface Prototype
type EmailTemplate interface {
   Clone() EmailTemplate
   Customize(subject, body string)
   GetContent() string
}

// Struct qui implémente le Prototype
type BasicEmail struct {
   Subject string
   Body    string
}

func (e *BasicEmail) Clone() EmailTemplate {
   return &BasicEmail{
      Subject: e.Subject,
      Body:    e.Body,
   }
}

func (e *BasicEmail) Customize(subject, body string) {
   e.Subject = subject
   e.Body = body
}

func (e *BasicEmail) GetContent() string {
   return fmt.Sprintf("Subject: %s\nBody: %s", e.Subject, e.Body)
}


package main

import (
	"fmt"
	"training.go/designpatterns/prototype/email"
)

func main() {
	// Prototype d'email générique
	baseEmail := &email.BasicEmail{
		Subject: "Bienvenue sur notre site",
		Body:    "Merci de vous être inscrit. Profitez de nos services !",
	}

	// Clonage et personnalisation pour un utilisateur spécifique
	userEmail := baseEmail.Clone()
	userEmail.Customize("Bienvenue Toto !", "Toto, merci de vous être inscrite. Voici votre guide de démarrage.")

	fmt.Println(userEmail.GetContent())
}

Le même exemple en PHP :


namespace Practice\DesignPatterns\Prototype;

interface EmailTemplateInterface {
   public function clone(): EmailTemplateInterface;
   public function customize(string $subject, string $body): void;
   public function getContent(): string;
}


namespace Practice\DesignPatterns\Prototype;

class BasicEmail implements EmailTemplateInterface
{
   private string $subject;
   private string $body;

   public function __construct(string $subject, string $body)
   {
       $this->subject = $subject;
       $this->body    = $body;
   }

   public function clone(): EmailTemplateInterface
   {
       return new BasicEmail($this->subject, $this->body);
   }

   public function customize(string $subject, string $body): void
   {
       $this->subject = $subject;
       $this->body    = $body;
   }

   public function getContent(): string
   {
       return "Subject: {$this->subject}\nBody: {$this->body}";
   }
}


declare(strict_types=1);

use Practice\DesignPatterns\Prototype\BasicEmail;

require "./vendor/autoload.php";

// Prototype d'email générique
$baseEmail = new BasicEmail("Bienvenue sur notre site", "Merci de vous être inscrit. Profitez de nos services !");

// Clonage et personnalisation pour un utilisateur spécifique
$userEmail = $baseEmail->clone();
$userEmail->customize("Bienvenue Toto !", "Toto, merci de vous être inscrite. Voici votre guide de démarrage.");

echo $userEmail->getContent();

Conclusion

J'espère que grâce à cet article vous êtes désormais plus à l'aise avec le design pattern Prototype. Vous pouvez retrouver mon article sur les patrons de conception ou les autres tutoriels sur Iterator, Strategy, Decorator, Chain of responsibility et Observer.