Java : quelques bons réflexes à avoir (3)

Java — Tags :, — admin @ 23:00

Autre petit réflexe à avoir, et ceci ne se limite pas seulement au Java… La plupart du temps, on fait nos tests de manière simple et logique :

  1. if (sString.equals("machaîne")) {
  2.     // faire quelque chose
  3. }

ou encore…

  1. if (iNb == 2) {
  2.     // faire quelque chose
  3. }

La encore, un bon réflexe à avoir est d’inverser le test, de manière à comparer notre constante / chaîne à notre variable :

  1. if ("machaîne".equals(sString)) {
  2.     // faire quelque chose
  3. }

ou encore…

  1. if (2 == iNb) {
  2.     // faire quelque chose
  3. }

En effet, en prenant cette habitude, on ne lèvera jamais d’exception de type NullPointerException lorsque les variables sont mauvaises.

Java : quelques bons réflexes à avoir (2)

Java — Tags :, — admin @ 22:31

Dans la suite des évènements, je suis tombé sur de nombreuses conversions de types. Elles étaient bien sur contrôlées, ou du moins… La plupart étaient codées comme ceci:

  1. public int strToInt(String sString) {
  2.    try {
  3.       int iNb = Int.parseInt(sString);
  4.       return iNb;
  5.    } catch (Throwable t) {
  6.    }
  7. }

On pourrait croire que le code est juste. D’ailleurs, il marchait encore une fois très bien… Lorsqu’on lui demandait de faire ce qu’on attendait de lui. En fait, ce code peut être assez dangereux: si une exception est levée, il n’y a aucun moyen de le savoir, aucun avertissement pour dire que iNb n’a pas été initialisé correctement. Et si sString était nul?

Ce genre de problème se retrouve souvent, et j’ai malheureusement vu énormément de blocs try/catch vides, laissant au futur utilisateur un code ne marchant que dans certains cas…

La gestion des exceptions est quelque chose d’assez méticuleux en Java. On ne veut pas trop alourdir le code avec des nombreux blocs try/catch. Toutefois, ce genre d’erreur assez classique engendre facilement des problèmes par la suite. De nombreuses fois, le développeur mange les exceptions en laissant vide les blocs catch, un peu comme si ils avaient été créé un peu par hasard.

Il faut au moins enregistrer quelque part que l’exception a été levée (log d’erreur, message…). Le bon réflexe à avoir est bien sur de renvoyer l’exception.

Java : quelques bons réflexes à avoir (1)

Java — Tags :, — admin @ 18:09

Depuis peu de temps, je suis arrivé sur un projet J2EE pratiquement terminé. Mon but était de comprendre le fonctionnement de l’équipe, la structure du programme et du projet, d’en analyser les atouts et les défaillances. Pendant cette période, j’en ai profité pour regardé rapidement le code source. J’ai décidé de cataloguer les quelques petits problèmes que je voyais, tout en y apportant des solutions. Ce sont des petits reflexes à avoir lorsque l’on travaille en Java.

Attention au ramasse-miette

Demandez à n’importe quelle personne ayant travaillé en C/C++ et évolué sur du Java la chose qu’il a préféré, et cette personne vous répondra le ramasse-miette. C’est vrai qu’en C# comme en Java, le ramasse-miette est quelque chose d’exceptionnel, un aspect un peu magique qui rend la vie plus facile et le code plus souple. On n’a tout à coup plus à tenir compte de la gestion de la mémoire, ce qui peut représenter un énorme gain de temps, de production, et d’apétit le soir en rentrant chez soi.

Toutefois, le ramasse-miettes ne va pas s’occuper de tout. En effet, il existe des objets (pointeurs, poignées sur les fichiers, sémaphores, etc…) qui ont leur propres fonctions .close(), .destroy() ou .release() qu’il ne faut pas oublier d’appeller. Bien entendu, tout est automatiquement nettoyé par le dernier finalizer lorsque le programme se termine… Mais bien souvent, il vaut mieux s’en occuper de suite. (Imaginez un serveur multi-thread qui fait une fuite à chaque requête HTTP, la mémoire sera vite débordée…)

Ainsi, je suis tombé sur ce code:

  1. public static Properties loadFile(String sFileName) throws IOException {
  2.    FileInputStream sInputStream = new FileInputStream(sFileName);
  3.    Properties oProps = new Properties();
  4.    oProps.load(sInputStream);
  5.    sInputStream.close();
  6.    return oProps;
  7. }

Ce code compile parfaitement. De plus, l’application tournait aussi bien. Seulement, que se serait-il passé si la fonction load(sInputStream) avait relevé une exception? Le close() n’aurait pas eu lieu, et on aurait eu une fuite… jusqu’au dernier finalizer.

En fait, la solution consiste à utiliser un bloc try/finally pour vérifier que la fonction ne renvoie pas d’erreur :

  1. public static Properties loadFile(String sFileName) throws IOException {
  2.    FileInputStream sInputStream = new FileInputStream(sFileName);
  3.    try{
  4.       Properties oProps = new Properties();
  5.       oProps.load(sInputStream);
  6.       return oProps;
  7.    } finally {
  8.       sInputStream.close();
  9.    }
  10. }

Ainsi, le close() se fera toujours dans de bonnes conditions. J’avoue que cette pratique n’est pas très facile à mettre en oeuvre, surtout qu’elle risque facilement d’imposer la lourdeur des blocs try/finally imbriqués. On pourrait aussi revenir à une solution qui s’apparente au C/C++ et conserver quelque part une trace des différentes allocations mémoire pour les libérer manuellement, mais on perd l’intéret du ramasse-miettes… A méditer!

Stack Overflow, le knol du dev?

Internet — Tags :, — admin @ 16:11

Depuis quelque temps, je parcours souvent certains blogs de développement plus ou moins intéressants… Parmi mes préférés, on peut trouver le blog de Jeff Atwood (Coding Horror) et celui de Joel Spolsky (Joel on Software).

Récemment, ces deux développeurs ont mis en place un site web pour les développeurs reposant sur un système de questions/réponses : StackOverflow.

Tout le monde peut y participer, proposer des question, donner des réponses, et voter pour la qualité des éléments donnés, de telle sorte que les meilleures réponses figurent toujours en premier. L’interface est épurée et rapide :

Chaque personne possède des droits de modifications et de gestion selon son historique de participation : L’attribution de points récompense les meilleures réponses ou les questions les plus perspicaces. Ainsi, la communauté de stackoverflow est en constante évolution, et les réponses sont toujours très bien formulées.

Chapeau pour ce travail bien fait…

L’ABAP, un langage qui fâche

SAP — Tags :, — admin @ 14:47

Je pense que je vais commencer une grande série d’article visant à dénoncer une des plus grande blague de l’informatique : le monde du SAP… Et de son langage natif l’ABAP.

SAP est une sorte de baleine, un truc immense et bien encré dans la plupart des grandes entreprises. Le logiciel a 30 ans, et il le montre bien. L’interface est absolument hideuse, l’ergonomie est encore pire… Mais avant de critiquer les aspects fonctionnels du programme, ce qui fera l’objet d’un autre article, je voudrais m’attarder sur quelques prises de tête avec le langage natif… L’ABAP…

Prise de tête numéro 2 (perte de 2h de travail à la recherche du bug) :
Une table interne contient les données suivantes :

  1. WERKS  |  ZUONR  |  XAABG
  2. SD01   |  13005  | X
  3. SD01   |  00065  |
  4. SD02   |  35792  | X
  5. SD03   |  15283  | X

Je devais compter le nb d’affectations (ZUONR) par division (WERKS), et ensuite faire un tri par rapport au code de comptabilisation (XAABG).
Il paraissait logique de parcourir la table, d’incrémenter un compteur qui se réinitialiserait à chaque fois que l’on rencontrait une nouvelle division. Fort heureusement, il existe en ABAP une commande AT NEW [champs] qui, placé dans une boucle, permet de détecter lorsque le champs change.

Mon code était donc simple (pour du sap):

  1. " Boucler sur la table, mettre chaque ligne dans ws_affect "
  2. LOOP AT wt_affect INTO ws_affect.
  3.   ww_count = ww_count + 1.
  4.   AT NEW werks.
  5.     " Traitement et compte "
  6.     PERFORM mywork USING ws_affect ww_count.
  7.   ENDAT.
  8. ENDLOOP.

Et pourtant… Il semblait que tout était juste, ayant utilisé le AT NEW précédemment.. Et pourtant!! Mon code ne fonctionnait pas!! Tout avait l’air de marcher jusqu’à ce que je rentre dans mon PERFORM ou les données de ma ligne était perdues… J’avais déjà mis une heure pour trouver ou était le bug…

Juste avant le AT NEW, ma ligne ws_affect contient :

  1. WERKS  |  ZUONR  |  XAABG
  2. SD02   |  35792  | X

Enfin, juste après cette commande, par magie,ws_affect devient :

  1. WERKS  |  ZUONR  |  XAABG
  2. SD02   |  *      | *

J’ai fini par comprendre qu’au sein d’un AT NEW, SAP détruisait tout ce qu’il y avait après le champs recherché, ce qui rendait impossible le traitement de cette ligne sans en avoir au préalable copié le contenu (ou l’index). Une seule question… Warum messieurs??

1&1, un service client plutôt distant…

Internet — Tags :, — admin @ 15:00

Après une surcharge au niveau des téléchargements sur notre petit espace gratuit que nous avait alloué Free, j’ai décidé de migrer vers un nouvel hébergeur. Ayant un ami chez 1&1 qui pouvait me parrainer, j’ai accepté d’aller chez eux. Notre ancien blog était localisé sur Free, et on avait acheté le domaine nathetnico.fr à partir de Gandi qui faisait une simple redirection. J’en ai profité pour faire migrer notre blog et ait commencé un transfert de domaine entre Gandi et 1&1.

3 semaines plus tard, ne recevant aucune nouvelle de la part de 1&1, j’ai décidé de prendre les choses en main et d’essayer de les contacter. C’est alors que je me suis heurté à un mur bien sourd! Le premier écran était pourtant assez encourageant!!

Leur numéro de téléphone étant assez cher, j’ai cherché, en vain, une adresse email pour leur écrire. Après de nombreuses recherches sur leur FAQ assez mal fait, je tentais de correspondre avec eux directement.

Voici ce que l’on rencontre lorsque l’on cherche désespérément une personne a qui demander de l’aide…

C’est alors que l’on doit rentrer un simple code de vérification pour pouvoir envoyer un email. Ce genre de code de vérification est d’habitude utilisé pour éviter que des scripts envoient des emails automatiquement (évitant ainsi que des malins envoient un peu trop d’emails à la fois). Mais la, j’avais l’impression que le service client avait plutôt utilisé ce procédé pour écarter toutes les personnes essayant de les joindre!! J’ai passé plus de 30 mins avant de me décourager, j’ai demandé à qq amis d’essayer mais sans résultat… On confond les 0 et les O, les 1, les l, les L, les |… Et vous, y arriverez-vous, ou trouverez vous le service client de 1&1 tout aussi distant que moi?

[edit] : ils ont apparemment modifié leur système et ça a enfin l’air de fonctionner…

Ecrire en Latex facilement

Etudes, Internet — Tags : — admin @ 17:08

Le Latex… Certains scientifiques l’appelleront leur “langage fétiche” car il leur permet entre autres de faire de très belles formules sur des pages web. En fait, le LaTeX est plutôt une collection de macro-commandes permettant une mise en forme du processeur de texte Tex. (en fait, le nom LaTeX vient de l’informaticien américain Leslie Lamport et du processeur Tex).

Il existe sur Internet un site très intéressant qui permettait de créer facilement ses formules mathématiques en Latex : http://www.codecogs.com/components/equationeditor/equationeditor.php

Vous pourrez ainsi visualiser le résultat automatiquement, puis directement écrire du Latex dans votre code HTML, en attendant que les tags deviennent un standard!!

Allez hop on va résoudre un petit problème sympa pour le plaisir :
Combien de fois (x) doit-on plier une feuille de papier pour atteindre la lune?
épaisseur d’une feuille (p) = 0.2mm
distance terre-lune (d) = 370000km
Ceci revient à résoudre l’inéquation suivante : p * 2^x > d
On passe au log en faisant attention à l’inéquation: x > \frac{\lg d - \lg p}{\lg 2} puis x > \frac{\lg (\frac{d}{p})}{\lg 2}

On fait l’appli numérique :

x > \frac{\lg (\frac{3.7*10^8}{0.0002})}{\lg 2}

Ce qui nous donne :

x >= 41 car x \epsilon \textrm{N}

Voila… Je vous laisse prendre une feuille de papier et la plier 41 fois pour atteindre la lune.

Ces longs raccourcis

C/C++ — Tags :, , — admin @ 9:58

Certains raccourcis peuvent parfois ralonger certaines routes. Par example, il est souvent requis d’échanger deux variables entre elles. Pour peu que cet échange doive avoir lieu au sein d’une boucle, disons pour un affichage fluide par ex., on va chercher à en optimiser l’échange.

De manière logique, on pense de suite à utiliser une variable intermédiaire :

  1. void main()
  2. {
  3.    int a = 0;
  4.    int b = 1;
  5.    int c;
  6.  
  7.    // swap utilisant une 3e variable
  8.    c = a;
  9.    a = b;
  10.    b = c;
  11. }

Cette méthode a beau avoir certains défauts, elle est en tout cas très clair.

Il y a pourtant plus court à écrire :

  1. void main()
  2. {
  3.    int a = 0;
  4.    int b = 1;
  5.  
  6.    // en une ligne!
  7.    a+=b-=a=b-a;
  8. }

Ou du moins c’est ce que l’on pourrait croire… Regardons le code assembleur généré par les deux corps de programmes précédents:

Swap à 3 variables :

  1. push        edi                         ; preparer de la place
  2. push        esi                         ; pour 3 variables
  3. push        ebx  
  4. cmp         dword ptr ds:[00312E08h],0
  5. je          00000011
  6. call        78ACB4AF
  7. xor         esi,esi                     ; a = 0
  8. xor         edi,edi                     ; b = 0
  9. xor         ebx,ebx                     ; c = 0
  10. xor         esi,esi                     ; a = 0
  11. mov         edi,1                       ; b = 1
  12. mov         ebx,esi                     ; c = a
  13. mov         esi,edi                     ; a = b
  14. mov         edi,ebx                     ; b = c

Swap en une ligne :

  1. push        edi                         ; preparer de la place
  2. push        esi                         ; pour 2 variables
  3. cmp         dword ptr ds:[003F2E08h],0
  4. je          00000010
  5. call        7963B4AF
  6. xor         esi,esi                     ; a = 0
  7. xor         edi,edi                     ; b = 0
  8. xor         esi,esi                     ; a = 0
  9. mov         edi,1                       ; b = 1
  10. mov         eax,edi                     ; eax = reg. temporaire
  11. sub         eax,esi                     ; eax = b - a
  12. mov         esi,eax                     ; a = b - a
  13. sub         edi,esi                     ; b = b - a
  14. add         esi,edi                     ; a = a + b

On économise peut-être du tps en n’allouant pas de place pour une troisième variable, on le paye plus tard. Au final, le swap en une seule ligne prendra légèrement plus de temps que le swap sur 3 lignes. Plus important encore, le swap en une ligne est beaucoup moins lisible… Autant donc travailler clairement!!

Remarque :
Il existe une version optimale d’un échange de deux variables, détaillée ici : http://en.wikipedia.org/wiki/XOR_swap . Certains diront que cette version n’est pas entièrement portable… Mais ca reste à prouver, je l’ai vu tourner un peu partout… La technique consiste à utiliser la commande XOR qui est plus rapide que la commande MOV, comme ceci:

  1. void main()
  2. {
  3.    int a = 0;
  4.    int b = 1;
  5.  
  6.    // avec des Xor :
  7.    a = a ^ b;
  8.    b = a ^ b;
  9.    a = a ^ b;
  10. }

Ce qui donne :

  1. push        edi  
  2. push        esi  
  3. cmp         dword ptr ds:[00212E08h],0
  4. je          00000010
  5. call        7974B4AF
  6. xor         esi,esi
  7. xor         edi,edi
  8. xor         esi,esi
  9. mov         edi,1
  10. xor         esi,edi
  11. xor         edi,esi
  12. xor         esi,edi

Encore une fois, la lisibilité n’est pas optimale, à moins d’être habitué à faire des XOR!

« Page précédente
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
(c) 2012 Random namespace | powered by WordPress with Barecity