Patrons de conception/Prototype


Le patron de conception prototype est utilisé lorsque la création d'une instance est complexe ou consommatrice en temps. Plutôt que créer plusieurs instances de la classe, on copie la première instance et on modifie la copie de façon appropriée.

Patron de conception
Catégorie : « Gang of Four »Création
Nom français : Prototype
Nom anglais : Prototype
Simplifier la création d'une instance d'une classe en recopiant un objet existant

Pour implanter ce patron il faut déclarer une classe abstraite spécifiant une méthode abstraite (virtuelle pure en C++) appelée clone(). Toute classe nécessitant un constructeur polymorphique dérivera de cette classe abstraite et implantera la méthode clone().

Le client de cette classe, au lieu d'écrire du code invoquant directement l'opérateur "new" sur une classe explicitement connue, appellera la méthode clone() sur le prototype ou passera par un mécanisme fourni par un autre patron de conception (par exemple une méthode de fabrique avec un paramètre désignant la classe concrète à instancier).

Structure

modifier

Le diagramme UML de classes est le suivant :

 
Diagramme UML des classes du patron de conception prototype

La classe Prototype sert de modèle principal pour la création de nouvelles copies. Les classes PrototypeA et PrototypeB viennent spécialiser la classe Prototype en venant par exemple modifier certains attributs. La méthode clone() doit retourner une copie de l'objet concerné. Les sous-classes peuvent hériter ou surcharger la méthode clone(). La classe utilisatrice va se charger d'appeler les méthodes de clonage de la classe Prototype.

Exemple de code en C#

modifier
public enum RecordType
{
    Car,
    Person
}

/// <summary>
/// Record est le Prototype
/// </summary>
public abstract class Record
{
    public abstract Record Clone();
}

/// <summary>
/// PersonRecord est un Prototype concret
/// </summary>
public class PersonRecord : Record
{
    string name;
    int age;

    public override Record Clone()
    {
        return (Record)this.MemberwiseClone(); // copie membre à membre par défaut
    }
}

/// <summary>
/// CarRecord est un autre Prototype concret
/// </summary>
public class CarRecord : Record
{
    string carname;
    Guid id;

    public override Record Clone()
    {
        CarRecord clone = (CarRecord)this.MemberwiseClone(); // copie membre à membre par défaut
        clone.id = Guid.NewGuid(); // générer un nouvel identifiant unique pour la copie
        return clone;
    }
}

/// <summary>
/// RecordFactory est la classe utilisatrice
/// </summary>
public class RecordFactory
{
    private static Dictionary<RecordType, Record> _prototypes = new Dictionary<RecordType, Record>();

    /// <summary>
    /// Constructeur
    /// </summary>
    public RecordFactory()
    {
        _prototypes.Add(RecordType.Car, new CarRecord());
        _prototypes.Add(RecordType.Person, new PersonRecord());
    }

    /// <summary>
    /// Méthode de fabrication
    /// </summary>
    public Record CreateRecord(RecordType type)
    {
        return _prototypes[type].Clone();
    }
}

Exemple de code en JAVA

modifier
/* Classe Prototype */
public class Cookie implements Cloneable
{
    public Cookie clone()
    {
        try {
            Cookie copy = (Cookie)super.clone();
            // Dans une implémentation réelle de ce patron de conception, il faudrait
            // créer la copie en dupliquant les objets contenus et en attribuants des
            // valeurs valides (exemple : un nouvel identificateur unique pour la copie).
            return copy;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

/* Prototype concrets à copier */
public class CoconutCookie extends Cookie { }

/* Classe utilisatrice */
public class CookieMachine
{
    private Cookie cookie; // peut aussi être déclaré comme : private Cloneable cookie;

    public CookieMachine(Cookie cookie)
    { 
        this.cookie = cookie; 
    }

    public Cookie makeCookie()
    {
        return cookie.clone(); 
    }

    public static void main(String args[])
    { 
        Cookie        tempCookie =  null; 
        Cookie        prot       = new CoconutCookie(); 
        CookieMachine cm         = new CookieMachine(prot); 

        for (int i=0; i<100; i++)
            tempCookie = cm.makeCookie(); 
    }
}

Exemples

modifier

Exemple où prototype s'applique : supposons une classe pour interroger une base de données. À l'instanciation de cette classe on se connecte et on récupère les données de la base avant d'effectuer tous types de manipulation. Par la suite, il sera plus performant pour les futures instances de cette classe de continuer à manipuler ces données que de réinterroger la base. Le premier objet de connexion à la base de données aura été créé directement puis initialisé. Les objets suivants seront une copie de celui-ci et donc ne nécessiteront pas de phase d'initialisation.