Je développe une application cliente et je cherche à améliorer la gestion des sauvegardes.
Je me pose la question : quelles classes faut-il créer, dans quel package et pour quel rôle ?
J’aimerai bien sur séparer la partie donnée et la partie interface.
Pour le contexte, il s’agit d’un editeur Merise (AnalyseSI pour ceux qui connaissent), il faut donc sauvegarder une table, un ou plusieurs graphique, plus d’autres trucs. Je prévois pour le moment deux formats de fichiers différents dont un en lecture uniquement et l’autre en lecture/écriture avec possibilité d’ajouter d’autres formats (XML, binaire, texte, avec ou sans compression…).
Pour l’organisation des packages, j’aimerai déjà différencier les parties suivantes:
o les données en elle-même
o les composants affichant les données
o la gestion des sauvegardes niveau Interface (afficher les boites de dialogues Ouvrir/Fermer avec les différents filtres possibles)
o la gestion des sauvegarde niveau données (appliquer tel filtre pour charger tel fichier dans tel format).
Ma question, est-ce que je n’ai rien oublier, et plus important comment faire pour faire cohabiter tout ce monde avec ma classe maitresse : AnalyseFrame qui représente la fenêtre principale de l’application et qui a accès à tout.
PS: Je développe en Java mais cette problèmatique de sauvegarde d’état par différents moyens et identique dans les autres languages objets.
Là, tu parles d’un modèle en couche, et c’est un sujet hyper vaste et assez complexe mine de rien. Si je puis me permettre, arrête de penser en terme de données, mais plutôt en objet. Tu ne manipules pas des données à sauvegarder, mes des objets, qui éventuellement peuvent persister mais ce n’est pas leur problème. Rien qu’en pensant ça, déjà on se retrouve avec deux couches:
métier
persistance
Je le répète et le souligne, ce n’est pas le boulot du métier de savoir comment il va être sauvegarder. On ne doit pas voir trainer la moindre méthode save2file, update ou whatever là dedans. C’est ta couche de persistance qui elle est capable de te retourner des objets pouvant gérer la sauvegarde, la mise à jour etc etc de ton métier. Ces deux couches ne doivent absolument pas être interdépendantes comme on s’en doute, et un des moyens d’éviter ça est l’injection de dépendance: ta couche métier définit des interfaces décrivant la manière d’accèder aux données, et tu les implémentes autant de fois que tu veux dans ta couche de persistance en fonction du format que tu veux (enfin, il y a un débat sur le sujet, est ce que la couche métier doit réellement contenir ces interfaces?).
Pour éviter d’avoir à déclarer explicitement l’objet d’accès aux données dans ton application, cherches du côté du pattern Abstract Factory.
Pour en venir à ton interface, c’est encore une nouvelle couche qui fait appel à ton métier bien sûr, et qui “injecte” donc les méthodes de sauvegarde au métier. Un pattern hyper connu pour les GUI est le MVC (model view controller), mais je crois que les différents modules java gérant l’affichage l’implémentent par défaut…
Il peut exister d’autres couches encore, mais déjà que je trouve que ce que j’ai écrit n’ai pas super clair, je vais arrêter là ^^
Sinon tu peux trouver quelques pistes de plus sur mon blog
Pour être franc, je n’ai pas tout saisi mais ce que je retiendrais en priorité cette phrase :
Je continue à chercher de mon coter et j’ai trouvé un pattern qui correspond à mes besoins pour le fait de sauvegarder dans plusieurs formats différents : Pattern builder. Ca ne résoud pas tout le problème mais c’est un début.
Oui désolé j’ai écris ça rapidement et j’ai concentré en quelques lignes le sujet de bouquins entier B)
Ceci dit, je te conseil tout de même de bien fouiller les quelques pistes que j’ai donné, parce qu’autant utiliser des design pattern, c’est bien, mais ça ne te garantie en aucun cas le fait que ton appli soit réellement objet. D’après ce que tu as décrit, j’ai vraiment l’impression que tu mélanges métier, données et affichage et que les responsabilités se baladent un peu partout. Les patterns sont des outils dans l’archi de ton appli, par l’archi en elle même.
P.S: je ne veux pas paraître agressif ou insultant hein ^^ Mais c’est juste que je trouve dommage de faire de l’objet si c’est pour toujours penser en terme de données. Et d’ailleurs, c’est un reproche que je fais à la fac, iut (du moins le mien), et pas mal d’autres formations. On nous montre des langages orientées objets tout en continuant à enseigner la vieille approche par données. On se retrouve donc à ne pas exploiter les réelles possibilités de l’approche, et à concevoir nos classes en fonction de notre MCD, alors que ça devrait être complètement l’inverse.
D’un point de vue méthodo, je te suggère de commencer par le modèle objet (ce que tu appelles données). Ca sera juste des classes qui contiennent des types primitifs (string, int …) ou des autres classes de ton modèle ; il ne doit pas y avoir beaucoup de méthodes dans ces classes (à part les getters/setters), tout au plus des méthodes de validation (mais pas de saveToDatabase ou autre)
Ensuite, je suggère de faire la couche de persistance (responsable de sauvegarder ton modèle en base, en fichier …)
Puis la couche de service (s’il y a lieu d’en avoir une), c’est la couche qui fait du traitement sur ton modèle
Et enfin l’interface utilisateur
Il s’agit d’une architecture en couche “standard” pour ce genre d’application ; en ce qui concerne le packaging, tu peux faire un package pour chacune de ces couches, et ensuite fais des sous-packages logiques au feeling (histoire de pas avoir 70 classes dans le package modèle par exemple)
L’idée de faire différente couche est bonne viewww, maitenent, j’ai du mal à voir comment faire cohabiter les couche avec Swing.
un exemple : je dois réalisé un dictionnaire d’information, une information est contient un nom et un type (version simple).
En objet : une classe Dictionary et une classe Information, la classe Dictionary contient une propriété List et la classe Information contient deux propriété String name, String type.
Seulement actuellement, je gère ça directement dans une TableModel et j’ai du mal à voir comment adapter mon code pour utiliser cette couche objet.
C’est cool tout ça B) , ca dégrossie bien l’architecture, ce que j’avais surtout peur c’est de partir en live vers des développements de 3 ans, mais au contraire je pense gagner pas mal de temps.
Il me reste plus qu’à comprendre l’API du composant JGraph et ca va le faire !
Je plussoie aussi, c’est le code de ton interface graphique qui doit s’adapter à tes objets et services metier, et non l’inverse. C’est bien plus simple de prendre le problème dans ce sens. Ta couche métier doit impérativement êter indépendante de celle de présentation. Comme ça, si un jour tu veux réécrire ton application en web/J2EE par exemple, tu récupères toute ta partie métier et services, et tu codes juste la partie ouaibe correspondant au framework que tu utiliseras.
J’ai écrit les classes Dictionnary, Information donnée par viewww et et réécrit ma classe DictionnaryTableModel, c’est très rapide à développer, ca surtout peut être améliorer très facilement !
Excellent B) Et bon courage pour le reste
Ton interface est faite entièrement à la mano, ou tu utilises un framework pour gérer les menus, barres d’outil … en tout cas le résultat est classe.
Truc qui a rien à voir, la gestion AWT/Swing a été améliorée dans Java6 (double buffering, anti-aliasing …). Je te conseille de continuer à développer en Java 5 (ou 4) pour des histoires de compatibilité, et de lancer ton appli (de temps en temps) avec un Java 6, parait que les améliorations (rendu, fluidité) sont assez nettes !
Je développe en Java 5, il y a trop d’amélioration intéressante pour garder la compatibilité Java 1.4 pour Java 6 effectivement, ils ont fait d’énorme progret en intégrant ENFIN! le double buffering et en améliorant les thèmes natifs plus pleins d’autres petits détails.
Et pour l’interface, c’est du 100% mano je n’ai jamais été convaincu par les différents outils existants. Pour ceux que ca intéresse, les bibliothèques utilisées sont : SwingX (www.swinglabs.fr), Synthetica pour le L&F, ButtonBar (http://www.l2fprod.com/).
n tiers/couches (avec n>=3) c’est bien, mais parfois 2 ca suffit pour eviter de faire un truc overengineered et de se prendre la tete pour rien B) Enfin comme pour tout, y a pas de regle, y a que des conseils qui sont a adapter/changer/bidouiller en fonction du contexte, des besoin et de plein d’autre choses. C’est pour ca que c’est un metier B)
Je viens de me poser la question de synchronisatin entre la couche objet et la couche de présentation, si un composant graphique change une propriété d’un objet, et que cet objet est présent dans plusieurs composant graphique. Est-ce que utiliser le pattern Observateur est la meilleur solution, cela voudra dire qu’il faut implémenter ce pattern dans la couche objet ?
[quote=“ZGoblin, post:15, topic: 32636”]Je viens de me poser la question de synchronisatin entre la couche objet et la couche de présentation, si un composant graphique change une propriété d’un objet, et que cet objet est présent dans plusieurs composant graphique. Est-ce que utiliser le pattern Observateur est la meilleur solution, cela voudra dire qu’il faut implémenter ce pattern dans la couche objet ?
Pardon, j’avais pas vu que tu avais posé une nouvelle question.
Donc oui, le design pattern “observer” est une bonne façon de résoudre le problème …
… mais ! (il y a toujours un mais B) ) …
attention au couplage introduit entre ta couche de présentation et ton modèle.
Il faut avoir en tête qu’un modèle objet doit toujours etre très faiblement couplé à tout le reste et si possible être juste composé de Pojo (juste des objets java avec propriétés et getters/setters). Donc quasiment aucun lien à Jdbc, Awt, Swing ou à n’importe quelle autre couche de ton modèle.
Cette règle, comme toutes les autres, est faite pour être transgressée (mais en connaissance de cause). Utiliser le pattern observer induit forcément une dépendance (donc un couplage) entre l’observer (ton interface utilisateur) et le sujet (ton modèle objet). Mais alors attention à faire ca proprement : par exemple avec une interface et une classe abstraite subject (dont toutes les classes de ton modèle hériteront) capables de faire un firePropertyChanged() … Parce que le “bound properties” proposé par Sun je trouve ca carrémént goret (un objet du modèle qui dépend de AWT, yierk …)
Après si t’es franchement motivé (ou que tu as juste envie d’apprendre), tu peux regarder ça.
Ca te permet d’implémenter le pattern observer de manière absolument non intrusive. Je dis pas que c’est la meilleure solution (attention à ne pas tomber dans l’usine à gaz, parfois le remède est pire que le mal), mais en tout cas ca peut t’initier à l’AOP
aaah je me demandais quand le sujet aller venir à l’AOP (remarque, je crois avoir parlé d’inversion de contrôle, donc on était pas loin ^^ ).
Oui c’est clairement un bon sujet, donc +1 à vieww, mais le sujet est difficile à maîtriser (et je n’ai clairement pas la prétention d’être un masta B) ).
EDIT: ah en fait j’ai parlé d’injection de dépendance, mais bon dans ma tête c’est la même chose.
Sinon après réflexion, j’ai changé un peu mon approche, j’ai préféré développer mes propres listener. Je trouve ça super pratique, très flexible et assez sexy.
J’ai donc ceci :
[codebox]public class Dictionary {
private List listInformations;
private final List dictionaryListeners;
public Dictionary() {
this.listInformations = new ArrayList<Information>();
this.dictionaryListeners = new ArrayList<DictionaryListener>();
}
public void add(Information information) {
listInformations.add(information);
for(DictionaryListener dictionaryListener: dictionaryListeners)
dictionaryListener.informationAdded(information);
}
public void remove(Information information) {
listInformations.remove(information);
for(DictionaryListener dictionaryListener: dictionaryListeners)
dictionaryListener.informationAdded(information);
}
public void clear()
{
listInformations.clear();
}
public Information get(int index) {
return listInformations.get(index);
}
public int size() {
return listInformations.size();
}
public void up(int index)
{
if (index > 1)
{
Information information = listInformations.get(index);
listInformations.remove(index);
listInformations.add(index - 1, information);
}
for(DictionaryListener dictionaryListener: dictionaryListeners)
dictionaryListener.informationListChanged();
}
public void down(int index)
{
if (index < listInformations.size() - 2)
{
Information information = listInformations.get(index);
listInformations.remove(index);
listInformations.add(index + 1, information);
}
for(DictionaryListener dictionaryListener: dictionaryListeners)
dictionaryListener.informationListChanged();
}
/*
* Events
*/
public void addDictionaryListener(final DictionaryListener dictionaryListener) {
if (!this.dictionaryListeners.contains(dictionaryListener)) {
this.dictionaryListeners.add(dictionaryListener);
}
}
public void removeDictionaryListener(final DictionaryListener dictionaryListener) {
this.dictionaryListeners.remove(dictionaryListener);
}
}[/codebox]
et cette interface :
public interface DictionaryListener {
public void informationAdded(Information information);
public void informationRemoved(Information information);
public void informationListChanged();
}
Dans mon application ca marche du feu de Dieu, j’ajoute une information dans mon model et ma table se met à jour automatiquement, dites moi ce que vous en pensser ?
Yep, bonne implem !
Mais le jour ou tu as un autre objet de ton modèle à “observer”, je penses que tu peux remonter toute la mécanique de “addListener” “propertyChanged” … dans une classe abstraite et une interface dédiées dont tout ton modèle héritera.
Voui, c’est vrai que l’IOC est un super pattern, surement celui que j’utilise le plus dans mes applis.
J’utilise Spring (comme tout le monde ?) à cet effet.