Aller au menu - Aller au contenu

Icône Le pattern DAO (2/2)

Mise à jour : 17/06/2009
Difficulté : Difficile Difficile Creative Commons BY-NC-SA
3 526 visites depuis 7 jours, dont 251 sur ce chapitre classé 45/786
Dans ce chapitre, nous allons voir comment peaufiner le pattern DAO afin que celui-ci puisse s'adapter à d'autres systèmes de sauvegarde...
Le but du jeu étant d'avoir le moins possible de changements le cas échéant !
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire

Le pattern factory

Nous allons aborder ici une notion importante : la fabrication d'objets !
En effet, le pattern DAO implémente aussi ce qu'on appelle le pattern factory.
Celui-ci consiste à déléguer l'instanciation d'objets à une classe.

En fait, une fabrique ne fait que ça ! ^^
En général, lorsque vous voyez ce genre de code dans une classe :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class A{
   public Object getData(int type){
     Object obj;
     
     //----------------------
     if(type == 0)
        obj = new B();
     else if(type == 1)
        obj = new C();
     else
        obj = new D();
     //----------------------
     obj.doSomething();
     obj.doSomethingElse();
   }
}

vous constatez qu'il y a une création d'objet et que ceci est conditionné par une variable. En fait, selon celle-ci, l'objet instancié n'est pas le même. Nous allons donc extraire ce code (celui entre commentaires) pour le mettre dans une classe à part :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.sdz.transact;

public class Factory {

	public static Object getData(int type){
	     if(type == 0)
	        return new B();
	     else if(type == 1)
	    	 return new C();
	     else
	    	 return new D();
	}
}


Du coup, maintenant, lorsque nous voudrons instancier les objets de la fabrique, nous utiliserons celle-ci.
Comme ça :

Code : Java
1
2
3
B b = Factory.getData(0);
C c = Factory.getData(1);
//...


Pourquoi faire tout ce ramdam ? Quel est le problème avec nos instances ?

C'est simple : en temps normal, nous travaillons avec des objets concrets, non soumis au changement. Cependant, dans le cas qui nous intéresse, nos objets peuvent être amenés à changer et j'irais même plus loin : le type d'objet utilisé peut changer !
L'avantage d'utiliser une fabrique, c'est que les instances concrètes (utilisation du mot clé new) se fait à UN SEUL ENDROIT !
Donc, si nous devons faire des changements, il ne se feront qu'à un seul endroit ! Si nous ajoutons un paramètre dans le constructeur, par exemple...

D'accord, on commence à comprendre l'intérêt de cette fabrique.

Je savais que vous comprendriez vite. je vous propose maintenant de voir comment ce pattern est implémenté dans le pattern DAO.

Fabriquer vos DAO

En fait, la factory dans le pattern DAO sert à construire nos instances d'objets d'accès aux données.
Du coup, vu que nous avons un super-type d'objet pour ces objets, nous savons quel type d'objet va retourner notre fabrique.

Image utilisateur


Voici le code de notre fabrique :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.sdz.dao;

import java.sql.Connection;

import com.sdz.connection.SdzConnection;
import com.sdz.dao.implement.ClasseDAO;
import com.sdz.dao.implement.EleveDAO;
import com.sdz.dao.implement.MatiereDAO;
import com.sdz.dao.implement.ProfesseurDAO;

public class DAOFactory {

        protected static final Connection conn = SdzConnection.getInstance();        
        
        /**
         * Retourne un objet Classe interagissant avec la BDD
         * @return
         */
        public static DAO getClasseDAO(){
                return new ClasseDAO(conn);
        }
        /**
         * Retourne un objet Professeur interagissant avec la BDD
         * @return
         */
        public static DAO getProfesseurDAO(){
                return new ProfesseurDAO(conn);
        }
        /**
         * Retourne un objet Eleve interagissant avec la BDD
         * @return
         */
        public static DAO getEleveDAO(){
                return new EleveDAO(conn);
        }
        /**
         * Retourne un objet Matiere interagissant avec la BDD
         * @return
         */
        public static DAO getMatiereDAO(){
                return new MatiereDAO(conn);
        }        
}


Et voici un code qui devrait vous plaire :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import com.sdz.bean.Classe;
import com.sdz.bean.Eleve;
import com.sdz.bean.Matiere;
import com.sdz.bean.Professeur;
import com.sdz.dao.DAO;
import com.sdz.dao.DAOFactory;


public class TestDAO {

        /**
         * @param args
         */
        public static void main(String[] args) {
                System.out.println("");
                
                //----------------------------------------------
                //On va rechercher des élèves
                //----------------------------------------------
                
                //On récupère un objet faisant le lien entre la base et nos objets 
                DAO<Eleve> eleveDao = DAOFactory.getEleveDAO();
                
                
                for(int i = 1; i < 5; i++){
                        //On fait notre recherche
                        Eleve eleve = eleveDao.find(i);
                        System.out.println("\tELEVE N°" + eleve.getId() + " - NOM : " + eleve.getNom() + " - PRENOM : " + eleve.getPrenom());
                }                
                
                System.out.println("\n\t************************************************");
                
                //On fait de même pour une classe
                DAO<Classe> classeDao = DAOFactory.getClasseDAO();
                //On cherche la classe ayant pour ID 10
                Classe classe = classeDao.find(10);
                
                System.out.println("\tCLASSE DE " + classe.getNom());
                
                //On récupère la liste des élèves
                System.out.println("\n\tCelle-ci contient " + classe.getListEleve().size() + " élève(s)");
                for(Eleve eleve : classe.getListEleve())
                        System.out.println("\t\t - " + eleve.getNom() + " " + eleve.getPrenom());
                
                
                //Ainsi que la liste des professeurs
                System.out.println("\n\tCelle-ci a " + classe.getListProfesseur().size() + " professeur(s)");                
                for(Professeur prof : classe.getListProfesseur()){
                        System.out.println("\t\t - Mr " + prof.getNom() + " " + prof.getPrenom() + " professeur de :");
                        
                        //Tant qu'à faire, on prend aussi les matières... ^^
                        for(Matiere mat : prof.getListMatiere())
                                System.out.println("\t\t\t * " + mat.getNom());
                        
                }
                
                System.out.println("\n\t************************************************");
                
                //Un petit essai sur les matières
                DAO<Matiere> matiereDao = DAOFactory.getMatiereDAO();
                Matiere mat = matiereDao.find(2);
                System.out.println("\tMATIERE " + mat.getId() + " : " + mat.getNom());
                
        }
}



Et voilà le résultat que nous donne ce code :

Image utilisateur


Vous pouvez être fiers de vous ! Vous venez d'implémenter le pattern DAO utilisant une fabrique.
C'était un peu effrayant, mais, au final ce n'est rien du tout... ^^

On a bien compris le principe du pattern DAO, même la combinaison DAO - factory.
Cependant, on ne voit pas comment gérer plusieurs systèmes de sauvegarde de données ?
Il faut modifier les DAO à chaque fois ?

Non, bien sûr... Le fait est que vous pouvez très bien avoir un type de DAO pour chaque type de gestion de données (PostgreSQL, XML, MySQL...). Le vrai problème, c'est de savoir comment récupérer les DAO puisque nous avons délégué leurs instanciations à une fabrique.
Vous allez voir, les choses les plus compliquées peuvent être aussi les plus simples. :)

D'une usine à une multinationale

Faisons le topo de ce que nous avons :
  • des objets métiers ;
  • une implémentation d'accès aux données ;
  • une classe permettant d'instancier les objets d'accès aux données.


Le fait est que notre structure actuelle fonctionne pour notre système actuel... Ah ! Mais ! Qu'entends-je, qu'ouïe-je ?
Votre patron vient de trancher ! Vous allez utiliser PostgreSQL ET du XML !

C'est bien ce qu'on disait plus haut... Comment gérer ça ?
On ne va pas mettre des if(){...}else{} dans la fabrique, tout de même ?

Ah ! Je vous arrête !
Vous entendez ce que vous dites :

Citation : Les ZérOs
On ne va pas mettre des if(){...}else{} dans la fabrique, tout de même ?

Vous voulez mettre des conditions afin de savoir quel type d'instance retourner : ça ressemble grandement à une portion de code pouvant être déclinée en fabrique !
o_O
Tu veux faire des fabriques de fabriques ? ?

Oui ! Notre fabrique actuelle nous permet de construire des objets accédant à des données se trouvant sur une base de données PostgreSQL. Mais la problématique maintenant est de pouvoir aussi utiliser des données provenant de fichiers XML...

Voici un petit schéma représentant la situation actuelle :

Image utilisateur


Et voilà ce à quoi on aspire :

Image utilisateur


Je pense que vous êtes tous d'accord pour dire que ces deux usines ont un processus de fabrication très similaire.
Par là, j'entends que nous allons utiliser les mêmes méthodes sur les objets sortant de ces deux usines.
Voyez ça un peu comme une grande marque de pain qui aurait beaucoup de boulangeries dans tous les pays du monde ! Cette firme a un savoir-faire évident, mais aussi des particularités : le pain ne se fait pas pareil dans tous les endroits du globe...
Pour vous, c'est comme si vous passiez commande directement au siège social qui, lui, va déléguer à l'usine qui permet de répondre à vos attentes !
Schématiquement, ça donne ceci :

Image utilisateur


Lorsque je vous dis ça, vous devez avoir une réaction quasi-immédiate : héritage - polymorphisme !
Ce qui va changer le plus, par rapport à notre ancienne fabrique, c'est que nous n'utiliserons plus de méthodes statiques, mais des méthodes d'une instance concrète, et pour cause : impossible de créer une classe abstraite ou une interface avec des méthodes statiques destinée à la redéfinition !

Donc, nous allons créer une classe abstraite pour nos futurs fabriques, celle-ci devra avoir les méthodes permettant de récupérer les différents DAO ET une méthode permettant d'instancier la bonne fabrique !
Je vous ai préparé un diagramme de classe, vous verrez mieux comme ça :

Image utilisateur


Je vous ai même préparé les codes sources :

Secret (cliquez pour afficher)

Classe AbstractDAOFactory.java :
Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.sdz.dao;


public abstract class AbstractDAOFactory {

	public static final int DAO_FACTORY = 0;
	public static final int XML_DAO_FACTORY = 1;

	/**
	 * Retourne un objet Classe interagissant avec la BDD
	 * @return
	 */
	public abstract DAO getClasseDAO();
	/**
	 * Retourne un objet Professeur interagissant avec la BDD
	 * @return
	 */
	public abstract DAO getProfesseurDAO();
	/**
	 * Retourne un objet Eleve interagissant avec la BDD
	 * @return
	 */
	public abstract DAO getEleveDAO();
	/**
	 * Retourne un objet Matiere interagissant avec la BDD
	 * @return
	 */
	public abstract DAO getMatiereDAO();
	
	/**
	 * Méthode permettant de récupérer les Factory
	 * @param type
	 * @return AbstractDAOFactory
	 */
	public static AbstractDAOFactory getFactory(int type){
		switch(type){
			case DAO_FACTORY:
				return new DAOFactory();
			case XML_DAO_FACTORY: 
				return new XMLDAOFactory();
			default:
				return null;
		}
	}
}


Classe DAOFactory.java



Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.sdz.dao;

import java.sql.Connection;

import com.sdz.connection.SdzConnection;
import com.sdz.dao.implement.ClasseDAO;
import com.sdz.dao.implement.EleveDAO;
import com.sdz.dao.implement.MatiereDAO;
import com.sdz.dao.implement.ProfesseurDAO;

public class DAOFactory extends AbstractDAOFactory{

	protected static final Connection conn = SdzConnection.getInstance();	
		
	public DAO getClasseDAO(){
		return new ClasseDAO(conn);
	}
	
	public DAO getProfesseurDAO(){
		return new ProfesseurDAO(conn);
	}
	
	public DAO getEleveDAO(){
		return new EleveDAO(conn);
	}
	
	public DAO getMatiereDAO(){
		return new MatiereDAO(conn);
	}	
}


Classe XMLDAOFactory.java



Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.sdz.dao;

public class XMLDAOFactory extends AbstractDAOFactory {
	
	public DAO getClasseDAO() {		
		return null;
	}
	
	public DAO getEleveDAO() {
		return null;
	}
	
	public DAO getMatiereDAO() {
		return null;
	}
	
	public DAO getProfesseurDAO() {
		return null;
	}
}



Vous devez y voir plus clair ; même si la classe XMLDAOFactory ne fait rien du tout, vous voyez le principe de base et c'est l'important !
Nous avons maintenant une hiérarchie de classes capables de travailler ensemble. :magicien:

Je reprends le dernier exemple que nous avions réalisé, avec un peu de modifications...

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import com.sdz.bean.Classe;
import com.sdz.bean.Eleve;
import com.sdz.bean.Matiere;
import com.sdz.bean.Professeur;
import com.sdz.dao.AbstractDAOFactory;
import com.sdz.dao.DAO;
import com.sdz.dao.DAOFactory;


public class TestDAO {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("");
		
		//----------------------------------------------
		//On va rechercher des élèves
		//----------------------------------------------
		AbstractDAOFactory adf = AbstractDAOFactory.getFactory(AbstractDAOFactory.DAO_FACTORY);
		//On récupère un objet faisant le lien entre la base et nos objets 
		DAO<Eleve> eleveDao = adf.getEleveDAO();
		
		
		for(int i = 1; i < 5; i++){
			//On fait notre recherche
			Eleve eleve = eleveDao.find(i);
			System.out.println("\tELEVE N°" + eleve.getId() + " - NOM : " + eleve.getNom() + " - PRENOM : " + eleve.getPrenom());
		}		
		
		System.out.println("\n\t************************************************");
		
		//On fait de même pour une classe
		DAO<Classe> classeDao = adf.getClasseDAO();
		//On cherche la classe ayant pour ID 10
		Classe classe = classeDao.find(10);
		
		System.out.println("\tCLASSE DE " + classe.getNom());
		
		//On récupère la liste des élèves
		System.out.println("\n\tCelle-ci contient " + classe.getListEleve().size() + " élève(s)");
		for(Eleve eleve : classe.getListEleve())
			System.out.println("\t\t - " + eleve.getNom() + " " + eleve.getPrenom());
		
		
		//Ainsi que la liste des professeurs
		System.out.println("\n\tCelle-ci a " + classe.getListProfesseur().size() + " professeur(s)");		
		for(Professeur prof : classe.getListProfesseur()){
			System.out.println("\t\t - Mr " + prof.getNom() + " " + prof.getPrenom() + " professeur de :");
			
			//Tant qu'à faire, on prend aussi les matières... ^^
			for(Matiere mat : prof.getListMatiere())
				System.out.println("\t\t\t * " + mat.getNom());
			
		}
		
		System.out.println("\n\t************************************************");
		
		//Un petit essai sur les matières
		DAO<Matiere> matiereDao = adf.getMatiereDAO();
		Matiere mat = matiereDao.find(2);
		System.out.println("\tMATIERE " + mat.getId() + " : " + mat.getNom());
		
	}
}


Et le résultat est le même qu'avant ! Tout fonctionne à merveille !
Ainsi, si vous voulez utiliser l'usine de fabrication XML, vous pouvez faire ceci :
AbstractDAOFactory adf = AbstractDAOFactory.getFactory(AbstractDAOFactory.XML_DAO_FACTORY); .

Voilà, vous en savez plus sur ce pattern de conception et vous devriez être à même de coder le reste des méthodes (insertions, mise à jour et suppression), il n'y a rien de compliqué : ce sont juste des requêtes SQL... ;)

Allez, et si nous allions faire un tour vers notre QCM ?

Q.C.M.

À quoi sert le pattern factory ?
Quel est l'un des avantages de ce pattern ?
Dans le pattern DAO, la fabrique peut-elle se résumer à une classe utilisant des méthodes statiques pour retourner des objets ?
Dans le pattern DAO, qu'est-ce qui permet de pouvoir utiliser plusieurs sources de données ?

Statistiques de réponses au QCM

Bon, je vous l'accorde, c'est plus difficile qu'il n'y paraît, mais ce n'était pas insurmontable...
Vous avez appris à utiliser un nouvel outil de conception pour structurer vos programmes... Utilisez-le avec sagesse ! :)
Chapitre précédent Sommaire

Partager

9 commentaires pour "Le pattern DAO (2/2)"
Note moyenne : 3.34 / 4 (178 votes)
Pseudo Commentaire
Hors ligne softdounia # Posté le 16/08/2009 à 19:55:45
A
Avatar

Ville : Alger
Pays : Algérie
Études : ETS Montréal

Ouuuf :-° la Sa case la baraque.

La suite--> Secret (cliquez pour afficher)
la persistance d'objet avec Hibernate c’est tous ce qui manque.

Au :honte: Maître cysboy. :ange:

Les 40e rugissants et les 50e hurlants. o_O --> Brainstorming --> CamelCase
Image utilisateur

-------------
"je crois qu'on ne peut mieux vivre qu'en chercant à devenir meilleur, ni plus agréablement qu'en ayant pleine conscience de son amélioration"
Socrate >_<
-------------
les meilleurs peintres se sont fait dire qu’ils n’étaient pas bon, aujourd’hui ce sont des Dieux!
-------------
RTFM :-° l'expression anglaise d'argot Internet Read the fucking manual (« Lis le foutu manuel ») ;
-------------
Mon CV en ligne ici
 
Hors ligne ypikahe # Posté le 26/02/2010 à 12:16:31

Bravo cysboy pour ces excellents tuto. C'est un vrai travail de ninja :ninja:

J'ai une question concernant le pattern DAO. Dans ton dernier exemple, tu dis
<citation nom="Ainsi, si vous voulez utiliser l'usine de fabrication XML, vous pouvez faire ceci : AbstractDAOFactory adf = AbstractDAOFactory.getFactory(AbstractDAOFactory.XML_DAO_FACTORY); ."></citation>

Mais ne faut il pas changer aussi changer les objets DAO ??
Remplacer
DAO<Eleve> eleveDao = adf.getEleveDAO();
par
XMLDAO<Eleve> eleveDao = adf.getEleveDAO();

En effet, d'apres le schéma, une DAOFactory créée des DAO<T> alors qu'une XMLDAOFactory créée, elle, des XmlDAO<T> o_O
Peut on vraiment changer de factory et tout de meme garder des objets DAO<T> ? La XmlFactoryDAO ne devrait elle pas générer, elle aussi, des objet DAO<T> ?

Au passage, n'y aurait il pas une erreur dans le dernier schéma UML ? La classe mère (en vert) devrait être AbstractDAOFactory (DAOFactory existant déjà) et ses méthodes getEleveDAO, GetClasseDAO,... ne peuvent pas prendre un objet Connection en paramètre car cela n'aurait pas de sens coté XML. :o

merci de tes réponses
Hors ligne jb00ster # Posté le 23/11/2010 à 18:08:59

merci cysboy pour vos tutos et je suis d'accord avec @ypikahe pour ses remarques!
et si je voulais ajouter une méthode propre a l'une de ces classe dao par ex ajouter une nouvelle methode a proffesseurDAO? apres comment l'appeler puisque on retourne toujours un type DAO qui n'as pas cette nouvelle méthode
bonne journée
Hors ligne cysboy # Posté le 23/11/2010 à 18:28:57
tout est bô dans l' info
Avatar
Groupe : Auteurs

Ville : Tremeur
Pays : France métropolitaine
Études : CNAM

salut,
Je suis d'accord avec vos remarques (ypikahe et jb00ster)... J'ai écrit ça peut-être un peu vite...

Citation : ypikahe
Mais ne faut il pas changer aussi changer les objets DAO ??
Remplacer
DAO<Eleve> eleveDao = adf.getEleveDAO();
par
XMLDAO<Eleve> eleveDao = adf.getEleveDAO();

En fait, il faudrait que je refasse mon schéma car il manque une petite couche d'abstraction entre les factory et les interfaces DAO...
il aurait fallu que je mette l'interface DAO<T> en type de retour des factory et qu'ensuite, on décline celui-ci en XMLDao ou SQLDao par exemple...
faut vraiment que je change ça. ^^


Citation : ypikahe
Au passage, n'y aurait il pas une erreur dans le dernier schéma UML ? La classe mère (en vert) devrait être AbstractDAOFactory (DAOFactory existant déjà) et ses méthodes getEleveDAO, GetClasseDAO,... ne peuvent pas prendre un objet Connection en paramètre car cela n'aurait pas de sens coté XML.


Encore raison... Sauf que pour la connexion, tu peux passer null... dans le pire des cas... :-°


Citation : jbOOster
et si je voulais ajouter une méthode propre a l'une de ces classe dao par ex ajouter une nouvelle methode a proffesseurDAO? apres comment l'appeler puisque on retourne toujours un type DAO qui n'as pas cette nouvelle méthode

Un petit cast et le tour est joué... ;)

Image utilisateurImage utilisateur Image utilisateur Image utilisateur Image utilisateur Image utilisateur Image utilisateur Image utilisateur Image utilisateur Image utilisateur Image utilisateur
 
Hors ligne SonOfGod # Posté le 01/11/2011 à 16:51:43

Dans ce code, je n'ai pas vu apparaitre Class.forname("driver").
est ce normal ?

Voir tous les commentaires