vendredi 29 mars 2013

MVC dans un form Master-Detail


Ce billet fait suite au billet posté sur MVC le 4 Janvier 2011

MVC est un design pattern relativement vieux, mais il faut reconnaître qu’il a été remis au goût du jour depuis quelques années déjà et ce en particulier grâce ou à cause de Java.
Dans ce billet je vais m’intéresser à : 
-A)          Pertinence de MVC
Un des problèmes majeurs du développement est que les possibilités de résoudre un problème sont généralement multiples.  Souvent a priori, il n’y a pas de bonne ou de mauvaise méthode, les dev se laissent aller à leur habitudes, leurs logiques propre (parfois très différentes d’un dev à l’autre, en fonction de leur expérience). Il est souvent difficile lorsque l’on s’attaque à un problème non connu de trouver la solution ou la stratégie qui sera la plus payante, sur le court ou sur le long terme. Souvent on se laisse aller à son bon sens, ce qui peut parfois être payant à court terme mais décevant sur le long terme ou pire, inutilisable lorsque le système grossit. Heureusement, nombre de développeurs et d’architectes ont formalisé leur expérience, leur stratégie applicable sur des problèmes concret de développement. Ce formalisme définit les design patterns.  L’un des avantages de MVC est qu’il est lui-même un design pattern s’appuyant sur le pattern Observer (View)-Subject (Model). Remarque que java.util définit aussi la notion d’observer : Observable (Model) – Observer(View) - Listener (Controller). 
Dans ce cas précis nous allons nous intéresser à la manière de modéliser les réponses d’un utilisateur à une interface graphique servant de vue et d’entrée sur les données contenues dans une base.

Un problème auquel tout développeur, qui se respecte,  a été confronté au moins une fois dans sa vie.
Dans le courant des années nonante et même plus tard, les IDE graphique permettait d’associer aux contrôles (widgets) d’une fenêtre, un code de réponse à un évènement. Ce formalisme, bien connu des utilisateurs VB, Delphi, .. est souvent connu sous le nom de programmation Event Driven.

Le  problème de cette approche est le couplage fort entre l’interface et le contrôle de flux. En effet, tant que le contrôle de flux ne dépend que d’une fenêtre cette approche fonctionne plus ou moins bien.
Toutefois, quand le contrôle de flux impacte plusieurs fenêtres, lorsque l’on veut, par exemple, répercuter les modifications provoquées par l'action sur une fenêtre dans les autres, on est souvent amené à faire des bidouilles de programmation pas très propres (euphémisme pour ne pas dire dégueulasses) qui au final transforme le code de contrôle des interfaces en magnifique spaghetti peu appétissant.

B)       L’approche MVC (pattern observer)
      Définition : Model View Controller.

Pour faire simple le controlleur répond aux évènements activés dans les vues, en réponse à cela il modifie le status du modèle, celui-ci répercute les changements sur les vues.

Avantage : Contrôle de flux est indépendant des vues,  ajouter de nouvelles vues n’impacte pas toujours  le contrôle de flux. Les vues ne se connaissent pas entre elles, ce qui évite des couplages entre vues et entre code de contrôle.  Le modèle n’update les vues que lorsqu’il a été modifié par le controlleur.

D'un point de vue design pattern, le model contient donc un pattern observer, au sens Java, il est un observable, tandisque la vue est un observer. On peut résumer cela par le formalisme UML.

Diagramme de Classe:
- La responsabilité de la vue est l'affichage du modèle et la capture des évènements liés à la vue (clic de souris, clavier, ...)
- La responsabilité du contrôleur est de répondre aux évènements engendrés dans la vue.
- La responsabilité du modèle est double d'une part récupérer et gérer les données en provenance d'une source de donnée (une DB par exemple) ET notifier les vues d'un changement d'état:

Diagramme de Sequence


Ce diagramme donne les séquences de création par rapport à l'application. On crée le contrôleur, puis le modèle, puis les vues en leur passant une référence sur le contrôleur qui contient le code de gestion des événements, ensuite on appelle la fonction registerObserver du modèle pour assigner une nouvelle vue.

Lorsqu'un utilisateur effectue une action sur la vue, l'applic envoit un évènement, la réponse à cet évènement se fait dans le contrôleur qui va modifier l'état du modèle, la modification de l'état du modèle va obliger celui-ci à notifier les vues qui l'observent de son changement d'état. Ainsi quelques soit la modification effectuée dans une vue, cette modification, si elle a changé l'état du modèle se répercutera dans toutes les autres.

Traduisons cela en Java et pour se faire choisissons un exemple, tout d'abord une vue contenant une liste de studyInf contenue dans un JList.
La seconde vue contiendra la description des champs du studyInf ainsi qu'une liste de boutons permettant, la sauvegarrde, ou le passage à l'élément suivant ou précédent, il est à noter que les évènements du JList impacteront la vue contenant le descriptif et les boutons.

1. Crée les modèles:
dans ce cas nous avons besoin de deux modèles : StudyInf et StudyInfListModel

StudyInf : est le plus simple puisqu'il contiendra les données provenant de la DB. Remarquez que dans ce tutorial nous n'allons pas nous étendre trop loin sur la manière de récupérer les données DB.... Le mieux est de créer un service se chargeant de l'accès à la base de donnée et qui renverra un modèle ou une liste de modèles... Cela pourrait faire l'objet d'un autre billet... ou pas.


public class StudyInf {
protected String signalAction,
signalDescription,
assignment;
protected Long studyId;
protected int
protocolStatus,
signalStatus,
timeDimId;
protected float sizeN,
totalImbalance;
protected String protocolId,
protocolName;
...
// add getter/setter
}

Il s'agit d'une simple classe POJO. Intéressons nous maintenant à la classe StudyInfListModel qui sera utilisée par l'objet JList et par qui toutes les actions de rafraîchissements transiteront.


public class StudyInfListModel extends DefaultListModel {

/**

*
*/
private static final long serialVersionUID = -1561852405765263848L;
private static class MyObservable extends Observable {
@Override
public void setChanged() {
super.setChanged();
}

}

// embed the Observable (java does not support multiple inheritance)
public MyObservable observable;
public StudyInfListModel() {
super();
this.observable = new MyObservable();
}

public void addObserver(Observer observer) {

this.observable.addObserver(observer);
}

public void notifyObservers(Object object) {

this.observable.setChanged();
this.observable.notifyObservers(object);
}
}


Comme on le voit, la classe hérite de DefaultListModel, ce qui est obligatoire si on veut l'utiliser comme modèle pour un JList.. Comme Java ne supporte pas l'héritage multiple, on va simplement incorporer un Observable dans cette classe Model. Et on va ajouter les méthodes utiles à un Observable : 
- addObserver et notifyObserver(). Il faut noter qu'avant d'appeler notifyObserver il faut appeler la méthode setChanged(); Sinon l'appel ne se fait pas, si le modèle n'est pas passé à l'état changé.

Comme cette méthode est définie comme protected dans Observable, elle n'est donc pas directement accessible par notre classe wrapper. Pour pâlier à cela on va créer la classe interne : MyObserver qui va exposer publiquement la méthode setChanged(); Comme vous le savez une méthode protected peut être appelée par la classe fille, et celle-ci peut donc mettre le membre appelant public...

2.) Créer le contrôleur

Voici une partie du code de notre contrôleur:


public class StudyInfController implements ActionListener,
ListSelectionListener {
protected List studyInfList;
protected StudyInfListModel studyInfListModel;
public void initializeStudyInfModel() {
       //TODO add the code here
      //....
}

public StudyInfController(StudyInfDao studyInfDao,
StudyInfListModel studyInfListModel) {
this.studyInfListModel = studyInfListModel;

initializeStudyInfModel();

}


public void actionPerformed(ActionEvent e) {
StudyInf studyInf;
if (e.getSource() instanceof Component) {
Component source = (Component) e.getSource();
if (source.getName().equals("btCommit")) {
                             // TODO add the code here}
                            // ...
}

public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
try {
// Retrieve the selected item.
StudyInf studyInf = (StudyInf) this.getModel().getSelected();
this.getModel().notifyObservers(studyInf);
} catch (Exception ex) {
}
}
}
}


Comme déjà expliqué le rôle de contrôleur est de répondre aux évènements générés par les vues et d'adapter le modèle en conséquence. C'est pourquoi la classe implémente deux interfaces : SelectionListener pour la JList et ActionListener pour les boutons.
Tout d'abord le constructeur va charger les données utiles à l'initialization du modèle. C'est durant la construction du controlleur que le modèle studyInfListModel sera chargé avec les données provenant de la DB.
Comme vous l'aurez remarqué les fonctions actionPerformed() et selectionChanged() ne font jamais directement un appel explicite à la vue étant donné que si les vues connaisssent le contrôleur celui-ci ne les connais pas. La plupart des actions se feront au travers du modèle qui lui impactera ses vue (notifyObservers()). Dans actionPerformed() les tests sur les contrôles cliqués se feront au moyen du nom de ceux-ci.

if (source.getName().equals("btCommit")) {...
}

Remarquez aussi que le modèle est passé en paramètre au contrôleur.

3.) Les vues:
D'une manière générale, nos vues ne sont rien d'autre que des JPanel.

3.1) La listview:
La première vue définie ici s'occuppe d'afficher la liste des studyInf et de permettre à l'utilisateur de les sélectionner.

public class StudyInfListView extends JScrollPane implements Observer {
private static final long serialVersionUID = 1L;
JList list;
public StudyInfListView(StudyInfController controller) {
super();

this.list =new JList(controller.getModel());
this.setViewportView(this.list);
controller.getModel().setListContainer(this.list);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.addListSelectionListener(controller);
list.setFont(new Font("Courier",Font.BOLD, 12));

}
public void update(Observable observable, Object arg1) {

this.list.revalidate();
}
}

Comme déjà expliqué, la vue va implémenter une interface Observer qui va nous fournir la fonction update, qui sera automatiquement appelée par le modèle.
La vue est constituée d'un ScrollPane qui va nous servir de pattern decorator sur la JList afin de lui ajouter des scrollbars.

On va ensuite créer la JList en utilisant le modèle contenu dans le contrôleur, en effet la vue peut contenir une référence sur le contrôleur (notamment dans le but de l'assigner comme classe de réponse aux évènements reçu par ses composants). Comme par exemple pour la vue représentant le modèle:


btCommit.addActionListener(controller);
btClose.addActionListener(controller);
btNext.addActionListener(controller);
btPrev.addActionListener(controller);

Remarquez l'utilisation de "revalidate()" pour réafficher la fenêtre updatée.


4) L'application:
C'est là que l'on va gérer la création des entités MVC et lier le tout ensemble:

   StudyInfListModel listInfModel = new StudyInfListModel();

    this.studyInfController=new StudyInfController(listInfModel);

    JPanel panelEdt = new JPanel();
        panelEdt.setLayout(new BorderLayout());

     StudyInfListView panelEdtTop = new StudyInfListView(  studyInfController);

    panelEdtTop.setPreferredSize(new Dimension(640,240));
      listInfModel.addObserver(panelEdtTop);


On crée le modèle
On crée le contrôleur auquel on passe le modèle.
On crée le panel principal en border Layout (Center contiendra la liste et South les composants text edit, label, combo ainsi que les boutons).
On crée la première vue "StudyInfListView" et on lui passe le contrôleur en paramètre. 
On informe le modèle qu'une vue est prête à l'observer : addObserver()

On fait de même avec la seconde vue et puis on active le tout:

  listInfModel.notifyObservers(null);

      panelEdt.add(panelEdtTop, BorderLayout.CENTER);
      panelEdt.add(panelEdtBottom, BorderLayout.SOUTH);

      this.getContentPane().add(panelEdt);

    this.pack();

D'abord on demande au modèle de rafrâichir ses vues fraîchement crées, puis on place les deux vues dans le panneau principal que l'on attache au JFrame de l'application (getContentPane) et puis on fait un pack.


 Une fois ceci fait l'application devrait créer les vues sur les modèles et permettre à l'utilisateur d'influer sur l'état des modèles aux moyens des contrôles placés sur les vues...

Voilà, l'exemple donné ici, n'est pas fonctionnel tel quel mais le but est de donner une idée, un cannevas permettant d'implémenter du MVC dans Swing.



mardi 26 mars 2013

Les stratégies de mapping d’héritage en hibernate

Tableau récapitulatif:

Il n’y a pas une stratégie meilleure qu’une autre, il y a des stratégies meilleures en fonction des situations.
Dans un cas GCP compliance, on peut se permettre de sacrifier quelque peu les performances en faveur de l’intégrité des données. J’opterai donc plus pour une stratégie : JOINED.
Hibernate n’est pas ce qu’il y a d’idéal pour du reporting ou encore de l’ETL, là il est toujours possible d’utiliser une solution annexe comme des vues, directement attaquées par du SQL natif. Evidemment, ça ne permet plus à l’application d’être DBAgnostique mais cela permet de régler des problèmes de performances.
La dernière approche serait évidemment la création d’une base de données OLAP dans laquelle on chargerait les données en vue du reporting.

Référence :


mercredi 20 mars 2013

EDC Challenges



Se dire que l'on peut transformer en cinq minutes une applic EDC standard (laptop) en une applic EDC mobile est à mon sens aller un peu vite en besogne.

D'une part les framework mobile et web classic sont généralement assez différents (jQuery vs jQuery mobile, ExtJS vs Sencha Touch).
D'une part, vu les limitations physiques des BYOD, il est généralement intéressant de proposer une solution spécifique pour mobile.
D'autre part, même si les concepts de base restent les même, les API changent tout de même sensiblement ce qui a des impacts sur le développement , la validation et la maintenance.
Une idée intéressante est la conception d'une application web utilisable par les BYOD mais là il faut se méfier de la stabilité des liaisons 3G et des pertes de connexions éventuelles.

Les BYOD deviennent communément répandus, toutefois un certain nombre de problème persiste quant à la stabilité des connexion. Une perte de connexion peut facilement signifier perte de donnée. Dans certains hopitaux les accès wifi sont coupés (BYOD sont inactifs), vu les trous de sécurité présent dans les BYOD, je ne serais pas étonné que certaines organization banissent purement et simplement, pour l'instant, ce genre d'équippement de leurs réseaux.
Il serait intéressant de pouvoir travailler sur une solution hybride (mixant du online et du offline).

L'un des challenge (outre la réplication correcte des données) pour un système offline est la validation des données. (Gardons en tête que la plupart des EDC travaille par query, chaque query à un coût parfois 100$). La validation des données permet de limiter le nombre de query.

Développer un tel système poserait toutefois des question par rapprt à DataSci patent mais cela implique des contraintes techniques non négligeables (gestion des transactions longues, réplications, ...). Ceci, doit être pris en compte dès le début et peut sensiblement allonger les temps de développement.

D'un autre côté voyons aussi ce que l'avenir réserve :
  • l'amélioration au niveau sécurité
  • la généralisation de la 4G qui rendra l'utilisation de BYOD aussi fiable qu'une connexion WiFi classique.
Perdre du temps à développer un système offline/online qui sera absolète dans deux ans n'est pas très intéressant non plus.

ICH E6 GCP

Les GCP (Good Clinical Practices) couvertes par l'ICH (International Conference on Harmonization) prennent une place prépondérantes dans la conduite d'essais cliniques mais aussi dans la manière dont on doit concevoir des applications liées au domaine clinique.

Bon nombre de ces GCP sont reprises dans les guidelines ICH E6 (E pour efficacy). En ce qui nous concerne il faudra retenir qu'une application clinique doit obéir à 6 règles fondamentales:

- Validation : Le système doit être validé suivant un processus qualité bien règlementé.
- SOPs : Les standard operation procedures sont les procédures permettant d'assurer la validation de l'application.
- Maintenir un Audit trail sur les données sensibles (cfr CDISC)
- Securité (HTTPS, authentication et autorisation)
- Backup régulier (un private cloud peut-etre une solution intéressante)
- Maintient du blinding : selon les règles GCP on ne peut jamais savoir ce qu'un patient a reçu comme traitement (control / actif). Cela signifie aussi que l'on ne peut pas prédire ce qu'un nouveau sujet entrant dans l'étude va recevoir.