Prototype

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
Le clonage n'est plus réservé au domaine des films science-fiction. Mais il n'est pas utilisé uniquement dans le monde réel. Le développement des applications en a aussi besoin.

Le monde des applications utilise le clonage dans un de ses patrons de conception appelée prototype (prototype). Comme le nom indique, il s'agit d'un prototypage d'un objet, c'est-à-dire d'un clone de cet objet.

A l'époque le prototype était très utile dans les situations où l'initialisation d'un objet est très gourmande en ressources. Car l'appel d'une méthode de clonage est différent de l'initialisation d'une instance avec le mot-clé new. Mais depuis la spécification de la JVM a changé et les coûts d'initialisation à travers le constructeur habituel ont baissé. La différence entre les deux modes est censée de ne plus être très significative. Elle sera peut-être plus marquante dans d'autres langages de programmation.

Par contre un grand avantage du prototype sur l'initialisation standarde est la possibilité de passer les valeurs d'une instance à l'autre. On peut imaginer qu'on effectue les opérations sur les entités de la base de données. On veut copier-coller une entrée, puis l'insérer dans la base avec un nouvel identifiant. Vu que l'identifiant est auto-incrementé, il nous suffit juste de transmettre les valeurs. Pour cela on peut utiliser la méthode clone(), définie dans l'interface Cloneable. A l'intérieur de cette méthode on précise le comportement de clonage, et donc aussi des valeurs que prendra l'objet copié dès le début.

Trois acteurs participent dans protoype :
- client : le demandeur du clonage d'un objet.
- prototype : déclaration de l'interface à cloner.
- concrete prototype (prototype concret) : l'implémentation de l'interface clonable.

Exemple du prototype
Pour comprendre ce patron de conception, prenons l'exemple du clonage des animaux.


class Animal implements Cloneable {
protected String name;

public void setName(String name) {
this.name = name;
}

@Override
public Animal clone() throws CloneNotSupportedException {
Animal animal = null;
try {
animal = (Animal) super.clone();
animal.setName(name);

} catch (Exception e) {
System.out.println("An error occured on cloning animal : " + e.getMessage());
}
return animal;
}
}

class Sheep extends Animal {
@Override
public String toString() {
return "I'm a sheep. My name is " + name;
}
}

class Dog extends Animal {

@Override
public String toString() {
return "I'm a dog. My name is " + name;
}
}

Cette partie représente celle du prototype. Au tout début on retrouve le prototype Animal, avec la méthode clone() implémentée. Les deux autres classes sont des prototypes concrets et héritent de la méthode clone() de la classe Animal.



class PetShopFactory {
private Animal sheep;
private Animal dog;
{{
sheep = new Sheep();
dog = new Dog();
sheep.setName("Walty");
dog.setName("Azor");
}};
public Animal getAnimal(String type) {
try {
if (type.equals("Sheep")) {
return sheep.clone();
} else if (type.equals("Dog")) {
return dog.clone();
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}


}

La fabrique est un des acteurs facultatifs pouvant participer dans le prototype. Ici on initialise les instances des clasess Sheep et Dog pour ensuite cloner celle qui est demandée par le client. Le client dont le code se présente ainsi :



String[] animals = new String[]{"Sheep", "Dog", "Dog", "Sheep"};
PetShopFactory petShop = new PetShopFactory();
for (String a : animals) {
Animal animal = petShop.getAnimal(a);
System.out.println("Found animal : " + animal);
animal.setName(""+new Random().nextInt());
System.out.println("The same animal with new name : " + animal);
}


Le résultat diffère, comme l'indique le code du client. En fait, au début du code client on récupère le clone d'une instance. On l'affiche sur l'écran. Ensuite, on change le nom de ce clone pour montrer qu'à la fin, on n'opère pas sur la référence vers le même objet, mais bien sur les références différentes. Et voici le résultat :

Found animal : I'm a sheep. My name is Walty
The same animal with new name : I'm a sheep. My name is -961931046
Found animal : I'm a dog. My name is Azor
The same animal with new name : I'm a dog. My name is -1132523117
Found animal : I'm a dog. My name is Azor
The same animal with new name : I'm a dog. My name is -680504456
Found animal : I'm a sheep. My name is Walty
The same animal with new name : I'm a sheep. My name is -1007835470


Prototype est donc un design pattern qui se présente en tant que remède pour les instances dont la création standard est exigeante. Dans certains langages, le clonage est plus optimisé que l'initialisation normale des objets. Et dans d'autres, même si la différence n'est pas flagrante, le prototype peut être un raccourci dans la création des instances devant porter les caractéristiques semblables.
Bartosz KONIECZNY 27-11-2013 06:21 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 Symfony1

Quelle méthode il faut utiliser afin de récupérer les paramètres d'une route dans la classe sfRoute ?

Imaginons la situation où l'on veut créer une nouvelle classe pour gérer les routes URL de notre application. Afin de faire cela, on devrait hériter de la classe sfRoute.

Les paramètres d'une requête peuvent être lus grâce à la méthode matchesUrl() de sfRoute. Par exemple :
public function matchesUrl($url, $context = array()) {
  $parameters = parent::matchesUrl($url, $context);
  if(count($parameters) > 1) {
    echo 'Vous pouvez faire ce que vous voulez avec les paramètres reçus';
  }
}