Dans ce tutoriel, nous appendrons pourquoi utiliser Prototype.
Nous le définirons puis l’analyserons au travers d’un exemple pratique.
Pré-requis : Avoir vu Matrix
Définition :
Prototype : Patron de conception utilisé lorsque la création d’instance est complexe ou consommatrice de temps. Plutôt que de créer plusieurs instances d’une même classe, on duplique la première en la modifiant de manière appropriée.
Exemple pratique :
On souhaite créer une armée d’agent Smith. Chacun des agents est identifié par un numéro.
Deux choix s’offre à nous : Créer chaque agent un par un ou en créer un et le dupliquer.
Création des agents un par un
Le code source ci-dessous nous permet de créer une armée d’agents Smith un par un.
from datetime import datetime import time startTime = datetime.now() class Prototype: def __init__(self, **attrs): print("Agent creating ...") time.sleep(1) self.__dict__.update(attrs) def __repr__(self): return "Agent {}".format(self.name) class PrototypeDispatcher: def __init__(self): self._objects = {} def get_objects(self): """Get all objects""" return self._objects def register_object(self, name, obj): """Register an object""" self._objects[name] = obj def unregister_object(self, name): """Unregister an object""" del self._objects[name] def main(): print("Test with agent creation ****") dispatcher = PrototypeDispatcher() names = ['Smith', 'Johnson', 'Lopez', 'Anderson'] for i, name in enumerate(names): agent = Prototype( name=name, category="agent", mission="kill neo" ) dispatcher.register_object('agent-' + str(i), agent) objects = dispatcher.get_objects() print("Before name change for agent 2".center(50, "*")) print(id(objects['agent-2'])) print(objects['agent-2']) print(id(objects['agent-3'])) print(objects['agent-3']) objects['agent-2'].name = "Jenkins" print("After name change for agent 2".center(50, "*")) print(id(objects['agent-2'])) print(objects['agent-2']) print(id(objects['agent-3'])) print(objects['agent-3']) if __name__ == "__main__": main() # Get time for creating objects print(datetime.now() - startTime)
Création par clonage
Le code source suivant crée une armée d’agent Smith par clonage.
import copy from datetime import datetime import time import random startTime = datetime.now() class Prototype: def __init__(self, **attrs): print("Agent creating ...") time.sleep(1) self.__dict__.update(attrs) def clone(self, **attrs): """Clone an agent and update inner attributes dictionary""" print("Agent creating ...") obj = copy.deepcopy(self) obj.__dict__.update(attrs) return obj def __repr__(self): return "Agent {}".format(self.name) class PrototypeDispatcher: def __init__(self): self._objects = {} def get_objects(self): """Get all objects""" return self._objects def register_object(self, name, obj): """Register an object""" self._objects[name] = obj def unregister_object(self, name): """Unregister an object""" del self._objects[name] def main(): print("Test with agent cloning ****") dispatcher = PrototypeDispatcher() prototype = Prototype() names = ['Smith', 'Johnson', 'Lopez', 'Anderson'] for i, name in enumerate(names): agent = prototype.clone( name=name, category="agent", mission="kill neo" ) dispatcher.register_object('agent-' + str(i), agent) objects = dispatcher.get_objects() print("Before name change for agent 2".center(50, "*")) print(id(objects['agent-2'])) print(objects['agent-2']) print(id(objects['agent-3'])) print(objects['agent-3']) objects['agent-2'].name = "Jenkins" print("After name change for agent 2".center(50, "*")) print(id(objects['agent-2'])) print(objects['agent-2']) print(id(objects['agent-3'])) print(objects['agent-3']) if __name__ == "__main__": main() # Get time for creating objects print(datetime.now() - startTime)
Félicitations, le tutoriel se termine, vous savez à présent pourquoi et comment utiliser ce patron de conception.
Intéressant. Attention, les 2 exemples sont inversés : clonage en 1er et instanciations en 2nd.
Je ne l’ai pas fait tourner pour voir les temps d’exécutions. Est-ce que deepcopy est plus rapide que la création d’une instance ?
Merci pour ton observation.
L’exemple pratique a été modifié pour faciliter la compréhension de Prototype.
Dans cette exemple, la réutilisation de l’instance déjà créée s’avère plus rapide que deepcopy.
Cependant, il y a une différence majeure.
Avec deepcopy :
Chaque agent créé est stocké en mémoire à une adresse unique.
ex : Anderson => 19878999, Lopez => 19878977
La modification du nom d’un agent affectera seulement l’agent concerné.
ex : Anderson -> Jenkins, Jenkins => 19878999, Lopez => 19878977
Réutilisation d’instance :
Chaque agent créé avec l’agent original pointe vers un espace mémoire commun.
La modification du nom d’un agent affectera les autres agents.