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:
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:
nom du package | description |
---|---|
com.hipopochat.appbase | le code métier de l'application |
com.hipopochat.persistance.fabrique | possède le type abstrait de la fabrique |
com.hipopochat.persistance.fabrique.impl | les différentes implémentations de fabrique |
com.hipopochat.persistance.dao | les différents type de DAO |
com.hipopochat.persistance.dao.impl | les différentes implémentations de DAO |
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:
Enregistrer un commentaire