6 févr. 2012

Les DAO

Cet article me sert surtout d'aide mémoire dans le cas où je suis amené à réutiliser les DAO. Data Access Object sont comme leur nom le suppose des objets d'acces aux données. Les DAO sont utilisés lorsqu'on doit rendre persistant des données. C'est en fait une couche entre le code métier de votre application et le pilote vers votre base de données. Par ce concept on essaye d'abstraire tout le code interne à la connexion à la base de données, l'acces, l'ajout, la mise à jour et la suppression de données, et fournir au code métier des primitives qui seront elles utilisées à la place. Ainsi le code métier n'aura qu'à s'occuper à demander des informations, les modifier, etc… sans se soucier de comment se connecter, ou si on a bien fermé la connexion à la fin. Aussi la puissance des DAO se situe dans le fait qu'on peut carrément abstraire le type de connexion, de ce fait le code métier est très peu impacté. Conjointement avec les DAO, on peut utiliser le design pattern de fabrique abstraite qui permet, pour un impact moindre sur le code métier, de choisir son type de connexion avant de récupérer les DAO spécialisés.


Cahier des charges

Je propose un exemple de conception de DAO avec les propositions suivantes:
  • une application permet de gérer une liste de tâches à faire
  • on veut rendre persistant les tâches
  • la persistance se fait via une BDD Oracle si on a internet
  • sinon par DB4O en local

La connexion

L'application encapsule une Collection<Tache>, et les Tache possèdent un titre ainsi qu'un contenu. Pour se connecter à Oracle, il nous faudra soit un driver JDBC seul, soit JPA qui est un wrapper (au final) d'un driver JDBC. J'opte pour le driver JDBC seul et je vais donc devoir créer la table par moi même.
1:  create table HIPOPOCHAT_TACHES(
2:    tache_id int primary key,
3:    titre varchar(30),
4:    contenu varchar(500));
Je ne mettrais pas le code de l'application ici, c'est la conception qui m'interesse.

La fabrique abstraite

L'application en elle même va vouloir récupérer les tâches existantes dans la base de données, en supposant que j'ai déjà codé la partie qui vérifie l'état de la connexion à internet, on a en pseudo code le comportement suivant:
1:  Si (connecté) {
2:    DaoFabrique.setType(DaoFabrique.horsLigne);
3:  }
4:  else {
5:    DaoFabrique.setType(DaoFabrique.enLigne);
6:  }
Maintenant il faut que j'explique ce qu'est que la DaoFabrique. En fait le petit bout de pseudo code précédent serra la seule partie consciente du type de persistance qui va être utilisé dans l'application. On utilise le design pattern de fabrique abstraite, qui veut qu'on lui donne un type de persistance, une fois qu'on lui à indiqué quel serra le type de persistance qu'on allait utiliser, on peut récupérer une fabrique de DAO, qui sauront utiliser ce type de persistance. Pour être plus clair, on indique à la classe DaoFabrique quel est le type de persistance voulu, et on récupère la bonne fabrique en conséquence:
1:  /* je renseigne la fabrique abstraite */
2:  Si (connecté) {
3:    DaoFabrique.setType(DaoFabrique.horsLigne);
4:  }
5:  else {
6:    DaoFabrique.setType(DaoFabrique.enLigne);
7:  }
8:  /* je récupère la bonne fabrique maintenant */
9:  DaoFabrique df = DaoFabrique.getInstance();
Le nom de la méthode getInstance() est totalement arbitraire, mais apparemment c'est le nom qu'on utilise conventionnellement. La fabrique qu'on vient de récupérer saura que dans le cas ou on est bien en ligne on utilisera la fabrique qui sait fabriquer des DAO passant par JDBC. Dans l'autre cas on aura la fabrique qui nous fournira les DAO sachant utiliser DB4O. Du côté du code, en fait DaoFabrique n'est qu'une classe abstraite possédant des méthodes static, ainsi ce sont ces méthodes qui permettent de modifier le type de DAO qu'on souhaite utiliser. La classe possède aussi une énumération de tout les types de persistance qu'elle propose, dans notre cas j'ai voulu donner plus de sémantique à l'énumération, mais j'aurais très bien pu avoir l'énumération {jdbc, db4o, jpa, mysql} au lieu de ce qu'on a actuellement {enLigne, horsLigne}. Donc DaoFabrique devra être étendue par DaoFabriqueEnLigne qui sera spécialisée dans les DAO utilisant JDBC. Aussi DaoFabriqueHorsLigne devra hériter de DaoFabrique et sera spécialisée dans les DAO utilisant DB4O. Ce design pattern utilise le polymorphisme pour ainsi masquer le type réel d'une DaoFabrique, et c'est très pratique ! Ainsi dans la méthode getInstance() on à le pseudo code suivant:
1:  switch(etatConnectivité) {
2:  case enLigne:
3:    return new DaoFabriqueEnLigne();
4:  case horsLigne:
5:    return new DaoFabriqueHorsLigne();
6:  default:
7:    // erreur...
8:  }

Des DAO spécialisés

Je propose la structure suivante pour les packages:

nom du packagedescription
com.hipopochat.appbasele code métier de l'application
com.hipopochat.persistance.fabriquepossède le type abstrait de la fabrique
com.hipopochat.persistance.fabrique.implles différentes implémentations de fabrique
com.hipopochat.persistance.daoles différents type de DAO
com.hipopochat.persistance.dao.implles différentes implémentations de DAO
Le package com.hipopochat.persistance.dao possèdera les interfaces de tout les DAO necessaires. Dans le package com.hipopochat.persistance.dao.impl on aurra les différentes implémentations suivant le type de persistance qu'on utilise. Dans cet exemple simple on n'aura que le DAO DaoTache, qui s'occupe de rendre persistant une Tache qu'on lui donne. Par exemple dans le cas ou on est connecté à internet, la DaoFabrique utilisée serra DaoFabriqueEnLigne, et elle possèdera la méthode suivante:
DaoTache getDaoTache();
On retrouve encore le polymorphisme qui permet de nuancer le type réel du DAO, car ici le DaoTache que va retourner DaoFabriqueEnLigne est bien un DaoTacheJdbc.

Le code métier

Du coté de l'application, il serra beaucoup plus aisé de parler à un DAO car on serra débarrassé de l'ouverture et la fermeture de la connexion à la base de données par exemple, mais aussi des contraintes liées à la configuration, si on est comme dans mon exemple contraint d'utiliser plusieurs types de persistances, il faut connaître la configuration de chacun, or ici le code métier serra le même, car on pourra généraliser la configuration en utilisation le design pattern de fabrique abstraite.

Conclusion

Même s'il peut être fastidieux d'écrire une couche entière, les bénéfices ne sont pas moindre, en effet la maintenance du code ainsi que l'évolutivité de celui ci est garanti, de plus le prise en charge par un autre programmeur est plus aisée car la couche va cacher le niveau inférieur (ou supérieur).

Aucun commentaire: