Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Design Pattern Abstract Factory

Compatible ActionScript 2. Cliquer pour en savoir plus sur les compatibilités.

Ce Design Pattern utilise à la base un modèle Singleton, vous permet de gérer des familles de class différentes, et de remplacer à tout moment l'une par l'autre, il s'assure que ces familles de class implémentent précisement certaines méthodes et détails précisement comme ces méthodes sont écrites. Il découle de cela que l'on peut ensuite effectuer du polymorphisme sur celles-ci.

On utilise ce Design Pattern dans différents cas, à vrai dire, il se révèle très pratique pour beaucoup d'architectures.

Dans ce tutoriel, nous nous servirons pour l'exemple, d'un gestionnaire de commandes, une commande pouvant contenir différents produits, parfois très différents les uns des autres, mais tout ces produits ont un prix à l'unité et une description spécifique.

Pré-requis

Il est nécessaire d'avoir :

  • AbstractObjet : une class abstraite ( elle définit les méthodes mais ne les implémente pas ).
  • IAbstractObjet : une interface pour la class abstraite ( s'assure à la compilation que les class qui étendent les méthodes définies dans la class abstraite les implémentent correctement ).
  • Manager : Une class qui va gérer l'ensemble des objets.
  • ObjetA : Une class qui étend AbstractObjet et implémente IAbstractObject
  • ObjetB : Une class qui étend AbstractObjet et implémente IAbstractObject

Il est important de savoir ce qu'est la surcharge de méthode et le polymorphisme, pour cela, vous pouvez lire les 2 tutoriaux concernant l'introduction aux class en AS2.

Implémentation

Voyons comment nous implémentons tout cela :

AbstractObjet

Cette class ne sert que de patron aux class ObjetA et ObjectB :

class AbstractObjet
{
	// On déclare les noms de méthodes qui doivent être implémentées
	public var getDescription : Function;
	public var getPrixUnitaire : Function;
	public var setCouleur : Function;
	public var getCouleur : Function;
	// Le constructeur est privé pour empêcher l'instanciation de cette class servant de patron.
	private function AbstractObjet ()
	{
	}
}

IAbstractObjet

Interface s'assurant que les ObjetA et ObjetB implémentent correctement les méthodes définies dans AbstractObject :

interface IAbstractObjet
{
 	// Liste des méthodes abstraites de la classe AbstractObjet.
	// doivent être implémentées par toutes les objets qui l'étendent
	public function getDescription (n:Number) : String;
	public function getPrixUnitaire () : Number;
	public function setCouleur (s:String) : Void;
	public function getCouleur () : String;
}

Manager

Cette class Singleton doit permettre de créer ou supprimer des occurences de ObjetA et ObjetB, elle sert de point d'accès central, elle permet aussi de faire du polymorphisme :

import AbstractObjet;
class Manager
{
	private static var _inst : Manager;
	// container pour stocker les objets
	private static var _aObjet : Array;
	private static var _aObjetQuantite : Array;
	// Variable pour le polymorphisme
	private static var _nPrix : Number;
	private function Manager ()
	{
	}
	public static function getMan () : Manager
	{
		if (_inst == undefined )
		{
			_inst = new Manager ();
 
			_aObjet = [];
			_aObjetQuantite = [];
			_nPrix = 0;
		}
		return _inst;
	}
	// Méthode pour ajouter un objet au manager
	public function addObjet (o : AbstractObjet) : Void
	{
		_aObjet.push (o);
		_aObjetQuantite.push (1);
	}
	// Méthode pour supprimer un objet du manager en fournissant son index
	public function delObjet (n : Number) : Boolean
	{
		// On vérifie que n est bien valide
		if (n < _aObjet.length && n >= 0)
		{
			// On supprime l'objet
			_aObjet.splice (n, 1);
			_aObjetQuantite.splice (n, 1);
			return true;
		} else 
		{
			trace ("ID fournie invalide")
			return false;
		}
	}
	// retourne l'instance de l'objet en fonction de l'index fourni
	public function getObjet (n : Number) : AbstractObjet
	{
		return _aObjet [n];
	}
	// retourne le prix total : polymorphisme
	public function getPrixTotal () : Number
	{
		_nPrix = 0;
		var l : Number = _aObjet.length;
		for (var i : Number = 0; i < l; i ++)
		{
			var o : AbstractObjet = _aObjet [i];
			_nPrix += o.getPrixUnitaire () * _aObjetQuantite[i];
		}
		return _nPrix;
	}
	// méthodes facultatives, pas nécessaires à partir du moment où l'on a getObjet
	// récupere la description d'un objet
	public function getDescription (n : Number ) : String
	{
		if (n < _aObjet.length && n >= 0)
		{
			var o : AbstractObjet = _aObjet [n];
			return o.getDescription (n);
		} else 
		{
			return "ERREUR";
		}
	}
	// change la quantité d'un Objet
	public function setQuantite (n : Number, nQuantite : Number) : Boolean
	{
		if (n < _aObjet.length && n >= 0)
		{
			_aObjetQuantite [n] = nQuantite;
			return true;
		} else 
		{
			return false;
		}
	}
	// fixe une couleur pour un objet
	public function setCouleur (n:Number, sCouleur : String ) : Boolean
	{
		if (n < _aObjet.length && n >= 0)
		{
			var o : AbstractObjet = _aObjet [n];
			o.setCouleur (sCouleur);
			return true;
		} else 
		{
			return false;
		}		
	}
	// donne la couleur d'un objet
	public function getCouleur (n:Number) : String
	{
		if (n < _aObjet.length && n >= 0)
		{
			var o : AbstractObjet = _aObjet [n];
			return o.getCouleur ();;
		} else 
		{
			return "ERREUR";
		}		
	}
}

ObjetA

Une des class faisant partie de la famille AbstractObjet :

import AbstractObjet;
import IAbstractObjet;
class ObjetA extends AbstractObjet implements IAbstractObjet
{
	private var _nPrixUnitaire : Number;
	private var _sDescription : String;
	private var _sCouleur : String;
	public function ObjetA ()
	{
		_nPrixUnitaire = 10.00;
		_sDescription = "ObjetA";
		_sCouleur = "Gris";
	}
	public function getPrixUnitaire () : Number
	{
		return _nPrixUnitaire;
	}
	public function getDescription (n:Number) : String
	{
		var s : String = _sDescription+ " n°"+n;
		return s;
	}
	public function setCouleur  (s:String) : Void
	{
		_sCouleur = s;
	}
	public function getCouleur () : String
	{
		return _sCouleur;
	}
}

ObjetB

Une des class faisant partie de la famille AbstractObjet :

import AbstractObjet;
import IAbstractObjet;
class ObjetB extends AbstractObjet implements IAbstractObjet
{
	private var _nPrixUnitaire : Number;
	private var _sDescription : String;
	private var _sCouleur : String;
	public function ObjetB ()
	{
		_nPrixUnitaire = 25.00;
		_sDescription = "ObjetB";
		_sCouleur = "Gris";
	}
	public function getPrixUnitaire () : Number
	{
		return _nPrixUnitaire;
	}
	public function getDescription (n:Number) : String
	{
		var s : String = _sDescription+ " n°"+n;
		return s;
	}
	public function setCouleur  (s:String) : Void
	{
		_sCouleur = s;
	}
	public function getCouleur () : String
	{
		return _sCouleur;
	}
}

Usage

Voyons voir maintenant comment cela fonctionne, et comment on s'en sert :

import ObjetA;
import ObjetB;
import Manager;
trace("Création du Manager")
var oMan : Manager = Manager.getMan();
// Ajout d'Objets dans le Manager
var oA : ObjetA = new ObjetA ();
trace("Prix dans le Manager : ")
trace (oMan.getPrixTotal())
trace ("Ajout de ObjetA")
oMan.addObjet (oA);
trace("Prix dans le Manager : ")
trace (oMan.getPrixTotal())
var oB : ObjetB = new ObjetB ();
trace ("Ajout de ObjetB")
oMan.addObjet (oB);
trace("Prix dans le Manager : ")
trace (oMan.getPrixTotal())
// on teste le fonctionnement :
trace (oMan.getDescription(0))
trace (oMan.getDescription(1))
trace ("tentative d'acces à une méthode sur un objet inexistant")
trace (oMan.getDescription(2))
trace ("Amusons nous avec getObjet")
trace (oMan.getDescription(0)+ " > "+oMan.getObjet (0).getCouleur());
trace (oMan.getDescription(1)+ " > "+oMan.getObjet (1).getCouleur());
var iA : AbstractObjet = oMan.getObjet (0);
trace ("Changement de couleur de l'objet 0")
iA.setCouleur ("bleu");
trace (oMan.getObjet(0).getDescription(0)+" > "+oMan.getObjet (0).getCouleur())
trace ("notez que il est préférable de passer par le Manager plutot que par l'objet, car l'on pourrait ecrire des erreurs :")
trace (oMan.getObjet(1).getDescription(0)+" > "+oMan.getObjet (0).getCouleur())
trace ("Là on est sur du résultat :")
trace (oMan.getDescription(0)+ " > "+oMan.getCouleur(0))
trace (oMan.getDescription(1)+ " > "+oMan.getCouleur(1))
trace ("Changeons les quantités : ")
oMan.setQuantite (0,30)
trace ("Prix total : "+oMan.getPrixTotal ())
trace ("on Supprime ObjetA ...")
oMan.delObjet(0)
trace ("Prix total : "+oMan.getPrixTotal ())
trace ("On rajoute 2 ObjetA")
var oA2 : ObjetA = new ObjetA ();
var oA3 : ObjetA = new ObjetA ();
oMan.addObjet (oA2);
oMan.addObjet (oA3);
trace ("Prix total : "+oMan.getPrixTotal ())
trace ("On refait un petit tour ... ")
trace (oMan.getDescription(0))
trace (oMan.getDescription(1))
trace (oMan.getDescription(2))
trace ("A vous d'ajouter au Manager les méthodes que vous voulez comme un getItemsLength, etc ... ")

En sortie :

Création du Manager
Prix dans le Manager : 
0
Ajout de ObjetA
Prix dans le Manager : 
10
Ajout de ObjetB
Prix dans le Manager : 
35
ObjetA n°0
ObjetB n°1
tentative d'acces à une méthode sur un objet inexistant
ERREUR
Amusons nous avec getObjet
ObjetA n°0 > Gris
ObjetB n°1 > Gris
Changement de couleur de l'objet 0
ObjetA n°0 > bleu
notez que il est préférable de passer par le Manager plutôt que par l'objet, car l'on pourrait écrire des erreurs :
ObjetB n°0 > bleu
Là on est sûr du résultat :
ObjetA n°0 > bleu
ObjetB n°1 > Gris
Changeons les quantités : 
Prix total : 325
on Supprime ObjetA ...
Prix total : 25
On rajoute 2 ObjetA
Prix total : 45
On refait un petit tour ... 
ObjetB n°0
ObjetA n°1
ObjetA n°2
A vous d'ajouter au Manager les méthodes que vous voulez comme un getItemsLength, etc ...