jeudi 6 décembre 2012

Principe ouvert/fermé (Open/close principle OCP).

Un petit post pour parler du principe ouvert/fermé (Open/close principle OCP).

D'abord la définition wikipédia :
http://fr.wikipedia.org/wiki/Principe_ouvert/ferm%C3%A9

"En programmation orientée objet, le principe ouvert/fermé affirme qu'une classe doit être à la fois ouverte et fermée. Ouverte signifie qu'elle a la capacité à être étendue. Fermée signifie qu'elle peut être modifiée par extension, sans modification de son code source. L'idée est qu'une fois qu'une classe a été approuvée via des revues de code, des tests unitaires et d'autres procédures de qualifications, vous ne voulez pas changer la classe mais seulement l'étendre. En pratique, le principe ouvert/fermé fait bon usage de l'abstraction et du polymorphisme."

Suivre l'OCP revient à écrire du code que l'on peut étendre sans avoir à le modifier!
Ça peut sembler incohérent, mais non, il n'y a pas d'erreur dans cette définition.

Contrairement au SRP, l'OCP n'est pas forcement un principe à appliquer à toutes les classes.
Il faut savoir identifier les fonctionnalités amené à souvent évoluer.
A partir de la le principe revient à créer un code qui sera amené à ne plus etre modifié, en faisant une abstraction de se qui évoluera.

Maintenant qu'on à défini l'OCP passons a un exemple, on va proposer une fonctionnalité qui consiste à calculer l'air d'un rectangle.

La classe Rectangle (sans les accesseur pour plus de lisibilité) :
  1. public class Rectangle {
  2.     private double width;
  3.    
  4.     private double height; 
  5.    
  6. }

La classe AreaCalculator avec sa methode de calcul d'air de plusieurs rectangles :
  1. public class AreaCalculator {
  2.    
  3.     public double area(List<Rectangle> shapes){
  4.         double area = 0;
  5.         for (Rectangle rectangle : shapes) {
  6.             area += rectangle.getWidth() * rectangle.getHeight();
  7.         }
  8.         return area;
  9.     }
  10.    
  11. }

Nous voila avec une solution qui devrait bien fonctionner. Mais, à peine quelques jours plus tard on vous demande la possibilité de calculer l'air de cercles en plus des rectangles.
Le client est roi et puis ça n'a rien de compliqué, alors on s'y colle en modifiant la classe AreaCalculator et en ajoutant une classe Circle :
  1. public class Circle {
  2.     private double radius;
  3.    
  4.    
  5. }

  1. public class AreaCalculator {
  2.    
  3.     public double area(List<Object> shapes){
  4.         double area = 0;
  5.         for (Object shape : shapes) {
  6.            
  7.             if(shape instanceof Rectangle) {
  8.                 Rectangle rectangle = (Rectangle) shape;
  9.                 area += rectangle.getWidth() * rectangle.getHeight();
  10.             }else{
  11.                 Circle circle = (Circle) shape;
  12.                 area += circle.getRadius() * circle.getRadius() * Math.PI;
  13.             }
  14.            
  15.         }
  16.         return area;
  17.     }
  18.    
  19. }

Voila, vous avez répondu à l'attente du client, il est content... Mais  il semble qu'il y a anguille sous roche.
En effet, on peut très justement se demander pourquoi se limiter aux rectangles et aux cercles, c'est d'ailleurs ce que se dit le client, il revient alors vers vous pour vous demander si ajouter le calcul de l'air d'un triangle serait compliqué.
Pour cela vous allez devoir modifier une nième fois la classe AreaCalculator au risque de casser ce qui fonctionne, la classe AreaCalculator n'est donc pas fermé à la modification.
Et pourtant il semble judicieux, étant donné le nombre de modification déjà apporté et les éventuelles modifications à venir, d'appliquer l'OCP sur AreaCalculator.
Pour se faire, nous allons créer un Interface Shape avec une methode area() se chargeant de calculer l'air d'un Shape.

  1. public interface Shape {
  2.    
  3.     public abstract double area();
  4. }

Maintenant on va faire en sorte que les classes Rectangle et Circle implémente l'interface Area :
  1. public class Circle implements Shape {
  2.     private double radius;
  3.     @Override
  4.     public double area() {
  5.         return  radius * radius * Math.PI;
  6.     }
  7.    
  8.    
  9. }
  1. public class Rectangle implements Shape {
  2.     private double width;
  3.    
  4.     private double height;
  5.     @Override
  6.     public double area() {
  7.        
  8.         return width * height;
  9.     }  
  10. }
A partir de ce modele, on peut réaliser un algorithme de calcul d'air capable de faire completement abstraction du type de "Shape" :

  1. public class AreaCalculator {
  2.    
  3.     public double area(List<Shape> shapes){
  4.         double area = 0;
  5.         for (Shape shape : shapes) {
  6.             area += shape.area();  
  7.         }
  8.         return area;
  9.     }
  10.    
  11. }
Dorénavant l'ajout d'une nouvelle forme géométrique ne passe plus par la modification de l'algorithme du calcul d'air, mais uniquement par l'ajout d'une classe implémentant l'interface Shape ce qui rendra cette nouvelle classe utilisable par l'algorithme de calcul d'air.
AreaCalculator et sa methode de calcul d'air est alors fermé a la modification et ouverte à l’évolution.

Entre autres les desgin pattern Strategy, Template method ou encore Visitor découlent directement de l'OCP.

vendredi 23 novembre 2012

Node.js ou comment avoir du javascript coté serveur.

C'est en cherchant quelques informations sur l'utilisation des WebSockets coté serveur en Java, que j'ai découvert nodeJS.
WebSocket est un protocole de communication définit dans HTML 5.
Cette nouveauté me semble etre une petite révolution, sans rentrer dans les détails les websocket créent un canal de communication bidirectionnel entre le client et le serveur sur le protocole HTTP qui lui avait l’énorme lacune d’être unidirectionnel ( requête client -> réponse serveur).
Du coup beaucoup de choses deviennent possible de façon standard !!
Que le serveur puisse interroger ou notifier les clients change assez radicalement les choses.

Pour en revenir à ma recherche, je voulais juste savoir s'il existait déjà des façons d'implmenter les WebSockets coté serveur en java.

J'ai d'abord lu cet article :

http://blog.zenika.com/index.php?post/2012/06/22/Les-WebSockets-en-pratique

Il s'agit d'un petit comparatif de quelques solutions pour utiliser les websockets.
L'auteur conclu que pour lui "node.js & Socket.IO" est la solution qui fait référence.

J'ai un peu honte de l'avouer mais c'est la premiere fois que j'entendais parler de node.js, et d'une technologie basé sur du Javascript coté serveur, je me suis senti obligé de creuser un peu.
A priori Node.js à pas mal de succès de plus la communauté node.js semble très active, avec de nombreux modules déja existant.
Nodejs utilise l'interpreteur javascript de google chrome, autrement dit : c'est rapide !

J'ai donc voulu essayé, pour ça j'ai suivi ce tutoriel :

http://nodejs.developpez.com/tutoriels/javascript/node-js-livre-debutant/

Et puis j'ai vu ce tutoriel vidéo qui m'a convaincu de la simplicité de nodejs + Socket.IO :

Créer un tchat avec NodeJS et Socket.IO

Ca parait tellement simple !!
Par contre ça passe par un apprentissage Javascript, je dois bien avouer que pour moi qui suis un pur développeur objet en java certaines lignes de code sont déconcertantes.
Mais je reconnais que ça peut parfois être élégant !

Je manque peut etre d'imagination mais je vois pas nodejs concurrencer JAVA coté serveur, je n'ose imaginer une couche métier en javascript.
J'imagine que le succès de nodejs vient à la base des developpeurs front end (html,css,javascript), qui peuvent ainsi s'exprimer coté serveur.

Faut reconnaître que c'est assez surprenant de voir que le javascript s'invite sur les serveurs, alors que Java lui disparaît progressivement coté client, quand on voit les possibilités de HTML5 même JavaFX va avoir du mal à se faire un place coté client.

vendredi 16 novembre 2012

Riche Internet Application (RIA) HTML5 vs JavaFX 2

Dernièrement je me documente un peu sur JavaFX2.
A l'epoque de sa sortie j'ai été étonné de voir l'investissement d'oracle sur JavaFX dont la version 1 avait fait un flop.
La deuxieme mouture à l'air de suciter pas mal d’intérêt, et je dois avouer que ce que j'ai vu m'a plu.
La page de démo de JavaFX :
http://www.oracle.com/technetwork/java/javafx/samples/index.html

Je vais donc garder un oeil sur le sujet, attendre quelques retour et probablement l'essayer histoire de me faire mon idée sur ce futur remplaçant de Swing, car JavaFX est bien présenté comme le remplaçant de Swing.

Avant de tester JavaFX, j'ai fais le tour du net pour voir ce qu'on en dit.

Et j'ai trouvé par hasard une entré de blog dont le titre a suscité ma curiosité:

When to use JavaFX 2 instead of HTML5 for a Rich Internet Application (RIA)?

L'auteur précise que pour lui une application internet riche est un application très centré sur le client, avec un chargement initial permettant d'offrir une interaction fluide avec l'utilisateur.
C'est pour cette raison qu'il ne considère pas les framework (orienté serveur) tel que Vaadin,JSF, Wicket ...etc comme des solutions pour des applications internet riche.
Dans ce domaine HTML5 à de l'avenir c'est inévitable et l'auteur est d'accord avec ça  par contre on est en droit de se poser la question sur JavaFX, mais le fait qu'il soit comparé à HTML5 est  plutôt flatteur pour JavaFX.

A noter que pour l'auteur, flash et silverlight sont mort, meme Adobe et Microsoft sont passé au HTML5.

voici les pour et les contres de HTML5 selon l'auteur du blog :

Les pour
 -Standard W3C
-C'est le futur, c'est indéniable !
-Aucun plugin n'est necessaire, peut etre utilisé sur tous les navigateurs (compatibles)
-Déja de nombreux composant et nombreuse fonctionnalités disponnible.
Les contres
-Développement HTML/Javascript au lieu de java => gros inconvénient pour des développeurs JAVA -Spécification pas encore finalisé (d’après la roadmap ça sera pas avant 2014!) -Les navigateur ne sont pas (encore) tous compatible -Demande un effort de développement supplémentaire pour être compatible multi-navigateurs (Même si certains framework javascript résolvent ce problème c'est un effort supplémentaire.
JAVAFX :
Les pour

-Offre une API java  => permet de tirer parti de vos compétences java et d'utiliser les librairies et fonctionnalités existantes.
-Offre la possibilité d'utiliser des Domain Specific Language (DSL) type Groovy (GroovyFX) ou Scala (ScalaFX)
-Un language optionnel de définition de "layout" (FXML) permettant de séparer la définition de l'interface du comportement => A vous de choisir codage d'interface en java ou en FXML.
-Meme environnement de developpement pour le coté serveur et le coté client (utile pour le debugging, refactoring ... etc.)
-Pas de problème de compatibilité de navigateurs
-Support de CSS comme en HTML
-HTML et Javascript peuvent etre integré dans une application JavaFX
-Swing et JavaFX peuvent être utilisé dans la meme application, donc les application swing existante peuvent évoluer vers JavaFX.
-JavaFX 2 propose une architecture unifié permettant de deployer un projet dans divers contexte (application standalone, application web ou java web start) .En attendant la possibilité de déployer sur téléphones mobiles.

Les contres

-Une JRE est nécessaire chez le client.
-L'auteur precise que JavaFX n'est pas open source, mais je pense que ca à évolué depuis.
-JavaFX n'est pour le moment autorisé sur mac qu'en version "developer preview"), ce n'est plus le cas depuis la version 2.1
-Idem pour Linux ce n'est plus le cas depuis la version 2.2
-Pas encore d'information sur JavaFX Mobile du moins je n'ai rien trouvé !
-Offre moins de composant et fonctionnalités que HTML5
-JavaFX est le futur remplacant de Swing, le developpement est différent à cause des nouveaux concept comme les animations. Mais ceci n'est pas vraiment un désavantage, cela reste plus facile d'apprendre à utiliser JavaFX pour un développeur java que d'apprendre le HTML et le javascript nécessaire pour faire quelque chose d’équivalent.

En résumé la conclusion de l'auteur c'est que HTML5 et JavaFX sont deux excellent moyen de développer une RIA.
Selon lui pour les applications web publique il parait difficile d'imposer JavaFX et l'installation de java chez tous les utilisateurs.
Mais pour les applications internes à une entreprise JavaFX a sans doute sa carte à jouer.

Dans l'ensemble, pour le moment je suis assez d'accord, d'autant qu'il souligne qu'une autre alternative pourrait venir de GWT et un éventuel support de HTML 5 qui pourrait bien mettre tout le monde d'accord :
http://www.google.com/events/io/2011/sessions/gwt-html5-a-web-developers-dream.html

GWT ou JAVAFX ?
Quoi qu'il en soit il semble que les développeurs Java attendent une solution "full JAVA", et ne souhaitent pas particulièrement acquérir des compétences pointu en HTML5 pour développer des RIA.

jeudi 15 novembre 2012

Changer de base dynamiquement dans une application web SPRING + HIBERNATE

Il y a quelques mois maintenant, on m'a demandé une fonctionnalité que je ne pensais pas pouvoir réaliser.
Il s'agissait de donner la possibilité à un utilisateur connecté à l'application web de changer de connexion de base de données mais rien que pour lui.
Autrement dit uniquement sur sa session web sans impacter les autres utilisateurs.
Cette fonctionnalité vient de la version précédente de l'application, une version client serveur en VB qui permettait à chaque poste client de se connecté a une base différente.

L'idée est que l'utilisateur peut vouloir se connecter à l'application pour travailler sur un base archivé qui n'est pas la base courante de travaille.
L'application doit proposer 3 bases d'archive en plus de la base courante.
Les bases d'archives sont une image de la base courante à un instant t , le mapping hibernate reste donc le même.

Très honnêtement j'ai tout de suite dit que ça me semblait irréalisable, SPRING chargeant le "DataSource"  à l'initialisation de la webapp je voyais pas comment le réinitialiser dynamiquement et encore moins comment réinitialiser le data source pour une unique session web sans impacter les autres utilisateurs connecté au même moment sur la même application, tout simplement parce que le data source est unique pour toute l'application.

J'ai fini par trouvé une solution qui fonctionne plutot bien, dans le contexte de cette application.

D'abord j'ai déclaré l'ensemble des "data sources" dans le fichier de configuration de SPRING "applicationContext.xml" :
<bean id="parentDataSource"  class="org.springframework.jdbc.datasource.DriverManagerDataSource"  abstract="true">
     <property name="driverClassName" value="org.postgresql.Driver" />
     <property name="username" value="******" />
     <property name="password" value="******" />
</bean>
 
<bean id="courante" parent="parentDataSource">
     <property name="url" value="url de connexion base courante" />
</bean>
 
<bean id="archive1" parent="parentDataSource">
     <property name="url" value="url de connexion base d'archive 1" />
</bean>
<bean id="archive2" parent="parentDataSource">
     <property name="url" value="url de connexion base d'archive 2" />
</bean>
<bean id="archive3" parent="parentDataSource">
     <property name="url" value="url de connexion base d'archive 3" />
</bean>
 
 
<bean id="dataSource" class="com.capjtel.vtp.metier.datasource.BDDRoutingDataSource">
    <property name="targetDataSources">
        <map key-type="com.capjtel.vtp.metier.datasource.BDDType">
            <entry key="COURANTE" value-ref="courante" />
            <entry key="ARCHIVE1" value-ref="archive1" />
            <entry key="ARCHIVE2" value-ref="archive2" />
            <entry key="ARCHIVE3" value-ref="archive3" />
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="courante" />
</bean>

On définit tous les paramètres de connexion dans la balise d'id "parentDataSource".
Ensuite je défini le dataSource à proprement parlé.
Dans l'attribut class je spécifie la classe BDDRoutingDataSource qui hérite de org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.
Il s'agit de la classe qui indiquera à Spring quel "DataSource" utiliser parmi les "targetDataSources" de type BDDType:
public class BDDRoutingDataSource extends AbstractRoutingDataSource {

 @Override
 protected Object determineCurrentLookupKey() {
  
  return BDDContextHolder.getBDDType();
 }



}
public enum BDDType {
 COURANTE,
 ARCHIVE1,
 ARCHIVE2,
 ARCHIVE3
}
public class BDDContextHolder {
 
 public static final ThreadLocal<BDDType> contextHolder = new ThreadLocal<BDDType>();

 public static void setCustomerType(BDDType bddType) {
  
  contextHolder.set(bddType);
 }
 
 public static void setCustomerType(String bddType) {
  
  if (bddType == null) {
   setCustomerType(BDDType.COURANTE);

  }else if(BDDType.COURANTE.equals(bddType)){
   setCustomerType(BDDType.COURANTE);

  }else if(BDDType.ARCHIVE1.equals(bddType)){
   setCustomerType(BDDType.ARCHIVE1);
   
  }else if(BDDType.ARCHIVE2.equals(bddType)){
   setCustomerType(BDDType.ARCHIVE2);
   
  }else if(BDDType.ARCHIVE3.equals(bddType)){
   setCustomerType(BDDType.ARCHIVE3);
   
  }
  
 }

 public static BDDType getBDDType() {
  return (BDDType) contextHolder.get();
 }

 public static void clearBDDType() {
  contextHolder.remove();
 }
 
 
}

La classe BDDContextHolder conserve le BDDType dans un ThreadLocal ainsi le BDDType est propre au thread courant. Concretement l'utilisateur choisi sa base de travaille, elle est stocké dans la session HTTP, puis dans un servlet Filter j'initialise le BDDContextHolder pour le thread courant avec le contenue de la session HTTP, ainsi chaque requete http de l'utilisateur a son propre data source.


mardi 13 novembre 2012

Démarrage du blog

Bonjour à ceux qui s'aventurent sur ce blog.
Pourquoi ce blog ? 
Et bien d'abord parce que je n'en avais pas. 
Ce qui est assez léger comme raison je dois bien le reconnaître.
Après tout, si je pousse le raisonnement, dans la mesure ou  je n'ai pas de t-shirt à l'effigie de Britney Spears je devrais m'en procurer un !
C'est "classe" non ? 

Plus sérieusement je fêtes ma première décennie d’expérience en tant que ingénieur de développement.
Ce blog est une sorte de cadeau que je me fais, je trouve que c'est nettement mieux qu'un t-shirt Britney.

Passionné d’internet depuis de nombreuses années, ce blog me permet de parler de ma veille personnelle et de mon quotidien professionnel autour de réflexions et commentaires sur les thèmes des nouvelles technologies.




Le principe de responsabilité unique ( SRP ou Single Responsability Principle)


Durant mon expérience professionnelle j'ai eu se que j'appelle des "haaa moments", ces moments ou j'ai compris un truc qui à changé ma façon de travailler de manière positive.

Avant même de se lancer dans l'apprentissage de divers design pattern plus ou moins complexe, a mon sens, il est important d'avoir bien en tête 4 principes de base en programmation orienté objet:

-Le principe de responsabilité unique
-Le principe "ouvert/fermé"
-Le principe de substitution de Liskov
-Le principe d'inversion de dépendance

La découverte de ces 4 principes est un de ces "haaa moments".
D'un seul coup j'ai compris que la plupart des design pattern que je connaissais trouvais leur origine dans ces principes.
Et que s'il ne fallait retenir que 4 choses en programmation objets il valait mieux retenir ces 4 principes plutôt que 4 design pattern aussi puissants qu'ils puissent être.
Car appliquer ces 4 principes mène forcement à mettre en place volontairement ou non des design pattern.

Celui qui nous intéresse ici est le principe de responsabilité unique, qui est probablement le plus intuitif à comprendre.

"There should never be more than one reason for a class to change." — Robert Martin, SRP

Lorsqu'un développeur est confronté a un problème complexe il le découpe en plusieurs problèmes moins complexe jusqu’à résoudre le problème de départ.
Il va donc coder une solution pour chaque sous problème afin d'obtenir un code résolvant le problème complexe initial.
Chacun de ces morceaux de code à la responsabilité d'un sous problème.
Le principe de responsabilité unique nous incite à n'attribuer qu'une seule et unique responsabilité par classe.

Pourquoi ?

Pour répondre simplement, c'est parce que chaque responsabilité peut être a l'origine de modifications.
Chaque responsabilité est une raison de changements.
Une multitude de responsabilités dans une même classe aboutirait donc à une multitude de raisons de modifier cette classe.
Et plus une classe a de raisons d’être modifié moins elle est stable, la modification d'une responsabilité pourrait aboutir a un dysfonctionnement d'une autre responsabilité (effet dominos).

Prenons un exemple concret:

Un client vous demande une application simple de gestion de sa clientèle.
L'idée est de pouvoir afficher une liste détaillé de ses clients (nom,prénom,age, adresse) en mode console et de pouvoir sauvegarder dans son fichier client actuel d’éventuel nouveaux clients.
On aboutit à une modélisation assez simple de la classe Client :




Vous vous lancez dans le développement, sans trop de difficulté vous parvenez au resultat attendu par le client.
Fier de vous, vous présentez le résultat au client, qui entre temps à compris que nous n’étions plus dans les années 80, il souhaitait finalement donc une application web, et qu'il aimerait utiliser une base de données plutôt que son vieux fichier ASCII...

Une solution plus évolutive donnerai quelque chose dans ce gout la :



ClientDao s'occupe de sauvegarder un client
ClientIHM s'occupe d'afficher une liste de client
Client est la représentation d'un client.

De cette manière, la représentation d'un client est indépendante de l'affichage et indépendante de la sauvegarde d'un client.
Le jour ou le client voudra changer sa façon d'afficher la liste de ses client il suffira de modifier clientsIHM sans impacter les autres classes.
Cette indépendance est la clé de la ré-utilisabilité.
Certes le nombre de classe est multiplié mais il s'agit de petite classe robuste et réutilisable qui rende le code plus lisible.

En conclusion il est souhaitable d'oublier "l'objet magique" capable de tout faire, car il deviendra très rapidement difficile à maintenir voir carrément obsolète dés les premières évolutions !

Je tacherai de parler des autres principes que j'estime important dans de prochains billets.
En attendant pensez au SRP !!!