InfOsaurus

Aller au contenu | Aller au menu | Aller à la recherche

lundi 19 mai 2014

Raconter son domaine avec F#

J'ai récemment ressorti des cartons un vieux projet perso en C# (un jeu) avec l'idée de le réécrire en F#. Voici peu ou prou ce que donnait la partie Ressources du jeu :


   1:      public enum TypeRessource
   2:      {
   3:          Or = 0,
   4:          Bois = 1,
   5:          Pierre = 2
   6:      }
   7:   
   8:      public static class Or
   9:      {
  10:          // méthodes statiques de commodité d'utilisation des ressources
  11:          // ex : 'Or.Pts(3)' au lieu de 'new Ressource(TypesRessources.Or, 3)'
  12:          public static Ressource Qte(int i)
  13:          {
  14:              return new Ressource(TypeRessource.Or, i);
  15:          }
  16:      }
  17:   
  18:      public static class Bois
  19:      {
  20:          public static Ressource Qte(int i)
  21:          {
  22:              return new Ressource(TypeRessource.Bois, i);
  23:          }
  24:      }
  25:   
  26:      public static class Pierre
  27:      {
  28:          public static Ressource Qte(int i)
  29:          {
  30:              return new Ressource(TypeRessource.Pierre, i);
  31:          }
  32:      }
  33:   
  34:      public class Ressource
  35:      {
  36:          private int quantite;
  37:   
  38:          public int Quantite
  39:          {
  40:              get { return quantite; }
  41:              set { quantite = value <= 0 ? 0 : value; }
  42:          }
  43:   
  44:          public TypeRessource TypeRessource { get; set; }
  45:   
  46:          public Ressource(TypeRessource typeRessource, int quantite)
  47:          {
  48:              TypeRessource = typeRessource;
  49:              Quantite = quantite;
  50:          }
  51:   
  52:          public static bool operator ==(Ressource res1, Ressource res2)
  53:          {
  54:              if (ReferenceEquals(res1, res2))
  55:                  return true;
  56:   
  57:              if ((object)res1 == null || (object)res2 == null)
  58:                  return false;
  59:   
  60:              return (res1.Quantite == res2.Quantite && res1.TypeRessource == res2.TypeRessource);
  61:          }
  62:   
  63:          public static bool operator !=(Ressource res1, Ressource res2)
  64:          {
  65:              if (ReferenceEquals(res1, res2))
  66:                  return false;
  67:   
  68:              if ((object)res1 == null || (object)res2 == null)
  69:                  return true;
  70:   
  71:              return (res1.Quantite != res2.Quantite || res1.TypeRessource != res2.TypeRessource);
  72:          }
  73:      }
  74:   
  75:      public class Ressources
  76:      {
  77:          private IList<Ressource> listeRessources = new List<Ressource>();
  78:   
  79:          public ReadOnlyCollection<Ressource> ListeRessources
  80:          {
  81:              get { return new ReadOnlyCollection<Ressource>(listeRessources); }
  82:          }
  83:   
  84:          public Ressources(Ressource ressource)
  85:          {
  86:              listeRessources.Add(ressource);
  87:          }
  88:   
  89:          public Ressources(Ressource ressource1, Ressource ressource2) : this(ressource1)
  90:          {
  91:              listeRessources.Add(ressource2);
  92:          }
  93:   
  94:          public Ressources(Ressource ressource1, Ressource ressource2, Ressource ressource3) : this(ressource1, ressource2)
  95:          {
  96:              listeRessources.Add(ressource3);
  97:          }
  98:   
  99:          public void Ajouter(Ressource ressource)
 100:          {
 101:              foreach (Ressource ressourcePresente in listeRessources)
 102:              {
 103:                  if (ressource.TypeRessource == ressourcePresente.TypeRessource)
 104:                  {
 105:                      ressourcePresente.Quantite += ressource.Quantite;
 106:                      return;
 107:                  }
 108:              }
 109:              listeRessources.Add(ressource);
 110:          }
 111:   
 112:          public void Ajouter(Ressources ressources)
 113:          {
 114:              foreach (Ressource ressource in ressources.ListeRessources)
 115:              {
 116:                  this.Ajouter(ressource);
 117:              }
 118:          }
 119:   
 120:          public void Retirer(Ressource ressource)
 121:          {
 122:              foreach (Ressource ressourcePresente in listeRessources)
 123:              {
 124:                  if (ressource.TypeRessource == ressourcePresente.TypeRessource)
 125:                  {
 126:                      ressourcePresente.Quantite -= ressource.Quantite;
 127:                      return;
 128:                  }
 129:              }
 130:          }
 131:   
 132:          public static bool operator ==(Ressources res1, Ressources res2)
 133:          {
 134:              if (ReferenceEquals(res1, res2))
 135:                  return true;
 136:   
 137:              if ((object)res1 == null || (object)res2 == null)
 138:                  return false;
 139:              foreach (Ressource ressource in res1.ListeRessources)
 140:              {
 141:                  if (!res2.ListeRessources.Contains(ressource))
 142:                      return false;
 143:              }
 144:   
 145:              foreach (Ressource ressource in res2.ListeRessources)
 146:              {
 147:                  if (!res1.ListeRessources.Contains(ressource))
 148:                      return false;
 149:              }
 150:              return true;
 151:          }
 152:   
 153:          public static bool operator !=(Ressources res1, Ressources res2)
 154:          {
 155:              return !(res1 == res2);
 156:          }       
 157:      }

Et la version F# :


   1:  module Resources
   2:   
   3:    [<Measure>] type Or
   4:    [<Measure>] type Bois
   5:    [<Measure>] type Pierre
   6:   
   7:    type Resources =
   8:      { Or : int<Or> 
   9:        Bois : int<Bois>
  10:        Pierre : int<Pierre> }
  11:   
  12:      static member (+) (r1:Resources, r2:Resources) =
  13:        { Or = r1.Or + r2.Or
  14:          Bois = r1.Bois + r2.Bois
  15:          Pierre = r1.Pierre + r2.Pierre }
  16:   
  17:      static member (-) (r1:Resources, r2:Resources) =
  18:        { Or = 
  19:            match r1, r2 with
  20:            | _ when r1.Or - r2.Or < 0<Or> -> failwith "Resources can't have negative values"
  21:            | _ -> r1.Or - r2.Or
  22:   
  23:          Bois = 
  24:            match r1, r2 with
  25:            | _ when r1.Bois - r2.Bois < 0<Bois> -> failwith "Resources can't have negative values"
  26:            | _ -> r1.Bois - r2.Bois
  27:          
  28:          Pierre = 
  29:            match r1, r2 with
  30:            | _ when r1.Pierre - r2.Pierre < 0<Pierre> -> failwith "Resources can't have negative values"
  31:            | _ -> r1.Pierre - r2.Pierre
  32:        }
  33:   
  34:      static member Zero =
  35:        { Or = 0<Or>; Bois = 0<Bois>; Pierre = 0<Pierre>; }

Addendum : merci à @oaz pour avoir indiqué à juste titre qu'il existait des implémentations plus expressives et concises de la solution en restant en C#.


Un conteur sachant compter


Personnellement, j'aime bien l'idée qu'en plus de produire du code qui marche et performant, le développeur doit être un technical storyteller qui raconte son domaine de la manière la plus expressive possible afin de transmettre le modèle à ses pairs. Et qui dit expressif dit un bon rapport signal/bruit. F# me parait être un gros progrès sur ce point, il suffit de comparer les deux blocs de code pour s'en convaincre.

  • Les unit of measure permettent d'exprimer facilement des quantités comme 5<Or> alors que pour arriver à un raccourci à peu près équivalent en C# (Or.Qte(5)), il faut bricoler des helpers ou des méthodes d'extension peu lisibles.
  • L'égalité structurelle de F# fait qu'il n'y a pas besoin de redéfinir l'opérateur ==, deux record types avec les mêmes valeurs seront nativement égaux, comme on s'y attend pour des Value Objects en DDD.
  • C# est moins compact et va nécessiter de faire le lien entre plusieurs endroits si les classes sont des fichiers différents, ce qui engendre un surcoût cognitif pour bien comprendre la base de code.
  • Les types F# sont immuables par défaut, ce qui est une bonne chose pour garder cohérents des Value Objects comme ceux présentés ci-dessus, voire même pour des Entités.

Ce ne sont que quelques exemples parmi une longue liste de caractéristiques qui facilitent la vie du développeur, et le bout de code ci-dessus un avant-goût de toutes les possibilités du langage.


Le choix des armes


La programmation fonctionnelle peut être un animal difficile à apprivoiser. D'un côté, il y a le buzz actuel autour des langages fonctionnels qui, si l'on en croit certains, sont une silver bullet relégant les autres approches au rang de dinosaures. Je les vois plutôt comme un outil supplémentaire, diablement efficace, mais sans doute pas multifonctions (ha, ha) au point de se substituer à tous les autres outils de la boîte. Une approche hybride me tente donc plus pour l'instant que les sirènes des radicalistes fonctionnels.

D'un autre côté, la plupart des ressources sur la programmation fonctionnelle nous perdent d'entrée de jeu dans un labyrinthe de théorie complexe. Entre closures, monads, monoïdes, partial application, currying, on peut facilement se retrouver noyé dans le jargon technique, pris dans une avalanche de briques de base sans pour autant comprendre comment construire un mur. Si on attaque le fonctionnel par le côté académique "classique", il est difficile au premier abord de distinguer une application concrète à nos problèmes quotidiens qui révolutionnerait tout sur le terrain – et c'est souvent comme ça, à tort ou à raison, que l'enthousiasme d'un programmeur se met en marche.

Personnellement, ce qui m'attire le plus dans F# n'est pas forcément les bénéfices qu'on associe typiquement à ce changement de paradigme. Ce n'est pas prioritairement de pouvoir faire de la programmation parallèle et définir des modèles de concurrence beaucoup plus simplement, même si ça reste un atout important. Ce n'est pas non plus la réduction drastique du nombre de lignes de code mis en avant par certains – même en C#, on peut écrire du code très compact (au point d'être illisible), ce n'est pas un but en soi. Ce qui m'intéresse en premier dans F#, c'est tout une somme de facilités fournies par le langage et le compilateur. Le système et l'inférence de types, les unit of measure, les object expressions, les type providers sont un vrai bonheur de productivité et de sécurité. Ce sont tous ces petits plus qui, comme le dit le célèbre créateur d'un autre langage, rendent le développeur heureux en lui permettant entre autres de mieux formuler son modèle du domaine.


Ressources


Dans la catégorie "ils en parlent certainement mieux que moi" :

samedi 17 août 2013

Trouble Is My Business

trouble_couv.jpgJotaro Fukamachi est détective privé. Logé dans le cabinet miteux d'une amie dentiste, son quotidien est fait de menu fretin - affaires de mœurs, personnes disparues, combines de yakuzas de bas étage. Armé de son flegme à toute épreuve, de son indégonflable ego et de ses dents (parfois avantageusement remplacées par un flingue de location), il sillonne les rues de Tokyo à la rencontre d'ennuis dont il a fait son gagne-pain mais dont sa carcasse de héros hard-boiled ne sort le plus souvent pas indemne. Eternel fauché, porté sur la boisson, père catastrophique d'une petite fille qu'il voit rarement, il reste un enquêteur acharné et jusqu'au boutiste ; au fond, un chevalier solitaire au grand coeur.

Taniguchi et Sekikawa ont mis dans Trouble is my Business tous les ingrédients du roman noir américain – l’enquêteur désabusé, la ville, figure omniprésente à la fois nourricière et poisseuse, imprégnée de violence, la femme fatale, les flics ripoux, l’ironie du sort, avec une pointe de parodie affectueuse à l'égard de leurs références d'outre-Pacifique. La série rend clairement hommage aux maîtres de ce genre littéraire : Chandler et son Marlowe, Dashiel Hammett et son Spade, mais aussi cinématographique. Chacun des 8-10 épisodes est construit comme un mini-film noir avec prologue, mise en chasse du détective qu'on retrouve en "voix off" à la première personne, un cocktail de scènes d'action et de mystère, et un dénouement qui en général vaut son pesant de cacahuètes.


trouble2.jpg trouble1.jpg


Trouble is my Business, comme tout bon pulp, se dévore en l'espace de quelques aller-retours dans les transports en commun. Avec son dessin old-school (loin des productions plus récentes de Taniguchi) mais diablement efficace, son papier subtilement cheap et sa mise en page soignée accompagnée de notes de l'éditeur français (belge ?), c'est un classique plein d'humour et de noirceur qui n'a pas pris une ride, une découverte savoureuse que je recommande aux amateurs de mangas et/ou polars.

trouble3.jpg

Les tomes 1 et 2 sortis au printemps 2013 sont disponibles chez Kana, le n°3 devrait paraître à la rentrée. C'est en tout 6 volumes parus au Japon de 1979 à 1994 qui seront adaptés en VF.

lundi 1 juillet 2013

TDD : un nécessaire retour aux sources ?

En parcourant les vidéos récemment sorties de la Norwegian Developers Conference 2013, j'ai été interpellé par une présentation de Ian Cooper portant ce titre curieux : "TDD, where did it all go wrong ?". Si l'intitulé est accrocheur, le contenu s'est révélé tout aussi intéressant.

Real (TDD) men don't fake


Ian affirme qu'il s'est produit un moment dans la pratique de Test Driven Development où la notion de test unitaire a perdu son sens initial. Selon lui, Kent Beck a toujours employé ce terme pour désigner un test isolé des autres tests et non pas un test où le module de code testé est pris isolément des autres modules.

(c) christgr http://www.sxc.hu/profile/christgr

En d'autres termes, on doit faire en sorte que l'exécution d'un test n'ait pas d'effet de bord sur les autres tests et que leur ordre d'exécution n'ait pas d'importance, mais il n'est généralement pas nécessaire de focaliser notre test sur un objet en le coupant de son environnement (c'est à dire en mockant ses dépendances). Bien au contraire, ce procédé nous contraindrait à entretenir une pléthore de test doubles tous plus fragiles les uns que les autres. Dès qu'on change quelque chose dans l'implémentation d'une classe, une dizaine de mocks très liés à cette implémentation risque de ne plus fonctionner.

Voici en résumé l'approche qu'il conseille d'adopter avec les tests :

  • On ne doit pas tester des détails d'implémentation mais des comportements. Ce n'est pas la volonté d'ajouter une nouvelle méthode ou une nouvelle classe à notre système qui doit déclencher l'écriture préalable d'un test, mais l'apparition d'un nouveau besoin, d'une nouvelle fonctionnalité.
  • Il est difficile pour notre cerveau de penser à la fois à résoudre un problème et à le résoudre avec une solution robuste. Les deux premières étapes du cycle de TDD (Red/Green) devraient être consacrées à trouver une implémentation qui fonctionne, même effrontément stupide et mal bâtie, et ce n'est pas avant l'étape de refactoring qu'on devrait songer à améliorer la qualité de cette implémentation, à appliquer des design patterns, à séparer les responsabilités.
  • On a tout de même le droit, parfois, de s'aider de tests unitaires très proches des détails d'implémentation pour chercher la juste conception (Cooper appelle cela "rétrograder"). Mais ces tests sont fragiles et il est sain de les supprimer une fois implémenté le code qui les fait passer.
  • La majorité des tests unitaires devrait se focaliser sur les use cases qui se situent, dans une hexagonal architecture, à la lisière du noyau applicatif.



Mocking on Heaven's Door


La critique portée par Ian Cooper sonne comme une charge contre l'abus de l'approche opposée, connue sous le nom de mockiste.

Par exemple, à l'extrême inverse de ce que propose la présentation de la NDC, Joe Rainsberger professe, pour tester un graphe d'objets, de vérifier la basic correctness de chacun d'entre eux en empêchant tout effet de bord extérieur grâce à une barricade d'interfaces toutes mockées.

(c) mterraza http://www.sxc.hu/profile/mterraza

Pour Joe, les bugs d'un système peuvent être détectés et éradiqués d'une manière très simple - tester au niveau micro la robustesse de chaque objet en vérifiant non seulement s'il communique de façon valide avec ses collaborateurs, mais aussi s'il satisfait son contrat lorsque ce sont ses voisins qui le sollicitent. Pour cela, il faut des tests vraiment atomiques collant au plus près à la conversation entre l'objet et ses pairs, et non pas des tests d'intégration qui ont tendance à être lents, fragiles, très complexes si on veut être exhaustif, bref, une arnaque.

Le courant mockiste est d'autre part historiquement représenté par Steve Freeman et Nat Pryce, tenants de la London School of TDD et connus pour leurs travaux sur le framework d'isolation JMock ou encore l'excellent Growing Object-Oriented Software, Guided By Tests. L'usage des mocks préconisé dans le livre est beaucoup plus large et fondamental que ce que recommande Cooper, puisqu'ils vont structurer une partie de l'approche outside-in où l'on avance en implémentant au fur et à mesure les interfaces mockées dans les tests précédents. Cependant, le livre invoque par endroits le même slogan "Unit test Behaviours, Not Methods" que Cooper utilise dans sa présentation, ce qui semble indiquer un brouillage des frontières sur certains points.

Where did it go wrong again ?


Qui a raison ? Est-ce encore une querelle de chapelle stérile, la bonne réponse se situant entre les deux ? Si l'intervention de Ian Cooper abonde en points valides et plein de bon sens (sur l'utilisation prématurée de design patterns, la fragilité de certaines suites de tests à forte teneur en mocks...), elle soulève aussi beaucoup de questions.

  • Quelle peut être l'étendue d'un refactoring sur lequel on ne pourra pas ajouter de tests ? Est-ce qu'on peut faire émerger tout un nouveau module voire une nouvelle couche applicative sans la tester ? Est-ce que cela signifie qu'avant de commencer à écrire les tests unitaires, notre architecture doit être connue, stable et fixée pour empêcher le refactoring de la remettre en cause ?
  • Peut-on renoncer à l'efficacité d'un test unitaire à maille très fine qui va pointer directement vers l'objet défectueux lorsqu'un bug se produit, et nous épargner une séance de debug ? Est-il possible de se contenter d'un échec de test haut niveau accompagné d'une trace applicative parfois cryptique ?
  • L'intervenant reste finalement très vague et général à propos de "don't test implementation details". Qu'est-ce qui est considéré comme un détail d'implémentation ? Une classe auxiliaire ajoutée pour séparer les responsabilités ? Un objet représentant une composante d'une règle métier dans notre couche Domaine ? Tout ce qui est "en-dessous" des objets de plus haut niveau dans un module donné ? Où se situe la limite ?
  • Les problèmes de tests épousant de trop près les contours du code de production sont-ils dûs à la nature même de l'approche mockiste, ou à des défauts liés à l'utilisation parfois maladroite et/ou la rigidité de ces doublures dans certains cas, à savoir : mocks stricts obligeant à les spécifier intégralement, tests remplis de détails non pertinents pour la vérification à effectuer ? Ceci n'est-il pas réparé avec les évolutions apparues récemment (automocking containers, nouveaux frameworks d'isolation plus souples...) ?
  • Et c'est une question annexe, mais comment Cooper peut-il être sûr de bien résumer ce que pense Kent Beck ? Certains passages de Test-Driven Development By Example paraissent conforter sa thèse mais d'autres propos sont plus nuancés :

"TDD is not a unit testing philosophy. I write tests at whatever scale I need them to be to help me make my next step progress. [...] 40% of the tests of JUnit work through the public API. 60% of them are working on lower-level objects. [...] Part of the skill of TDD is learning to move between scales." (vers 22:00)

(c) klsa12 http://www.sxc.hu/profile/klsa12
Un autre développeur, Jason Gorman, semble choisir une approche pragmatique similaire sur laquelle on pourrait conclure :

Our goal here is reliable code and good internal design, which means that both schools of TDD are at times necessary - the Classic school when we're focused on algorithms and the London school when we're focused on interactions.

dimanche 30 septembre 2012

Visual Studio 2012

La version finale de Visual Studio 2012 étant sortie très récemment, j'ai pu le prendre en main et le tester pendant quelques heures. Je vous propose une petite visite guidée et commentée de cette nouvelle mouture de l'IDE.


Look & feel

Quand le sage lui montre la lune, le fou regarde les majuscules


C'est probablement le sujet qui a fait le plus jaser à la sortie de la RC de Visual Studio 2012 il y a quelques mois. Beaucoup se sont d'abord indignés contre les menus tout en lettres capitales dans la barre du haut. Franchement, je trouve la controverse un peu surfaite - les menus sont un tout petit détail de l'IDE, personnellement je vais assez peu dedans et le passage en majuscules ne m'a pas du tout gêné une fois la surprise initiale passée.

Penchons-nous plutôt sur la charte graphique et l'aspect général de cette nouvelle version.

Je trouve la page de démarrage simple, claire et lisible. C'est grosso modo la même que VS 2010 avec des informations mieux organisées et plus de matière sur les nouveautés de l'IDE. L'onglet Vidéos contient notamment des tutoriels instructifs sur ces dernières.


Page de démarrage

Dans tout l'IDE, Microsoft a fait le choix d'une interface graphique de type "ligne claire" à fort contraste avec très peu de couleurs et des zones carrées à la Metro Style. Les bords et les espaces non remplis, autrefois de couleur foncée, sont maintenant sensiblement de la même couleur que toutes les autres zones (éditeur de texte, explorateurs...) c'est à dire blancs ou gris très clair dans la charte par défaut. J'avoue que j'ai du mal avec ce style très lumineux (high key dirait-on en photographie) qui m'éblouit un peu. Les petits icônes, généralement noirs ou très peu colorés, sont aussi souvent peu reconnaissables et mal choisis.


Thème clair

J'ai donc essayé le thème sombre également proposé de base et... c'est un peu l'excès inverse, je suis vite revenu à l'autre.


Thèeme sombre

Du côté des performances, j'ai constaté une amélioration nette par rapport à VS 2010, que ça soit au lancement ou en termes de réactivité globale de l'IDE. Je n'ai pas non plus fait l'expérience de crashes intempestifs comme avec son prédécesseur à sa sortie. Tout paraît plus fluide - grâce à l'interface allégée ? Cela reste néanmoins à vérifier dans un contexte d'utilisation intensive, avec de nombreux fichiers ouverts, des designers UI, des tests unitaires et des recherches qui tournent, etc.

Au final, en bon adepte du dépouillement, je salue l'effort des concepteurs de VS 2012 pour simplifier l'habillage de l'outil et se concentrer sur l'essentiel ("La perfection, ce n'est pas quand il n'y a plus rien à ajouter mais plus rien à enlever", disait quelqu'un). Il va juste falloir trouver un thème plus confortable pour les yeux sensibles.


Navigation et éditeur de texte

Le retour de la vengeance des pin's


Autant le dire tout de suite, comparé à VS 2010, les innovations ne sont pas énormes en matière de navigation et d'édition du code dans ce cru 2012. On y trouve :

  • Une fonction "preview" très pratique. Il suffit de simple-cliquer sur un fichier dans l'explorateur de solutions pour afficher celui-ci comme si on l'avait ouvert. Simplement, son affichage est temporaire, un fichier en preview chasse l'autre. L'onglet du fichier preview s'affiche à droite, pour le distinguer des fichiers déjà ouverts. C'est une excellente fonction que je me suis retrouvé à utiliser énormément.


Fonction Preview

  • L'épinglage de fichiers, plus anecdotique. Les fichiers épinglés restent de façon permanente à gauche de la rangée d'onglets et ne sont jamais masqués même lorsqu'on a énormément de fichiers ouverts en même temps.


Pin's !

  • Un icône "Tout réduire" est maintenant présent par défaut dans l'explorateur de solution pour replier complètement l'arbre de la solution. Ce n'est pas trop tôt, cette fonctionnalité quasi-obligatoire à mon avis devant auparavant être ajoutée via un add-in ou une macro.


  • Surlignage de la ligne actuelle : elle peut paraître mineure, mais cette fonction apporte un confort non négligeable en nous indiquant instantanément où le code va s'insérer si on continue à taper au clavier. En effet, lorsqu'on part se balader de part et d'autre dans le fichier on perd souvent ce repère et le curseur n'est pas toujours clairement distinguable. Dommage que la ligne surlignée soit assez peu visible en thème clair, et plutôt trop visible en thème foncé.


Il y a aussi la fameuse zone de Lancement rapide qui se présente comme un champ de recherche "environnemental" pour retrouver un menu, une action, un fichier récemment ouvert...


Lancement rapide

L'idée est bonne sur le papier mais 95% de mes actions dans l'IDE étant des tâches familières déclenchées par des raccourcis clavier, j'en ai eu très peu l'utilité. Pour chercher dans des fichiers, je préfère utiliser l'excellent Ctrl-virgule apparu dans VS 2010. L'avenir nous dira si cela change.

En revanche, les manques de fonctions de productivité déjà signalés dans la version 2010 (mais pourtant présents depuis longtemps dans des outils tiers comme Resharper) sont toujours là :

- Toujours pas de "add reference and use"
- Toujours pas de fermeture automatique d'accolades
- Pas de suggestion de noms de variable
- Pas de goto base/inheritor depuis l'éditeur de texte
- etc.

Cette absence d'évolution est particulièrement criante dans le domaine du refactoring où il faut vraiment chercher pour trouver une nouveauté, à part peut-être une entrée de menu pour chercher de la duplication de code sur le code actuellement sélectionné (baptisé de manière amusante rechercher les clones en français).
Pendant ce temps, JetBrains nous ajoute des refactos à la pelle, dont le fameux Extract Class -Resharper reste à des années-lumières de ce que l'environnement de Microsoft sait faire par défaut.


Tests unitaires

Un petit pas pour le Shim, un grand pas pour l'humanité


Selon l'aveu même de représentants de Microsoft, dans un exercice d'auto-critique assez rare pour être souligné, l'équipe de Visual Studio s'est rendu compte que l'environnement de tests de 2010 était orienté testeurs et très peu pratique pour les développeurs.
En effet, le même type de projet Visual Studio servait pour les tests qualité et les tests unitaires des développeurs. Le test runner de VS 2010 était tout sauf confortable car lent, avec une interface verbeuse et peu visuelle obligeant à raisonner selon une notion de "liste de tests" peu familière. Tout cela a été corrigé dans VS 2012 où on a maintenant :

Un test runner digne de ce nom.

Il s'inspire bien sûr fortement des runners déjà existants, que ça soit celui de TestDriven.net, celui de Resharper ou d'autres. On y retrouve l'habituelle barre rouge ou verte ainsi que des pastilles des mêmes couleurs pour chaque test. A noter qu'on peut enfin écrire de nouveau tests et relancer le runner directement, il les prendra en compte sans besoin de passer par une fastidieuse opération d'ajout de tests à la liste. VS 2012 va même plus loin avec quelques fonctionnalités supplémentaires à saluer :

  • Une option permettant d'exécuter automatiquement les tests après chaque build. On est "presque" dans le continuous testing.
  • Dans ce mode, les tests les plus susceptibles d'échouer (ceux qui étaient déjà rouges, etc.) sont réexécutés en premier et les tests qui étaient verts ne sont pas réexécutés si d'autres échouent. On est "presque" dans l'intelligent test running à la JUnit Max.
  • On peut analyser la couverture de code d'un ou plusieurs tests.
  • Des raccourcis clavier sont présents de base pour exécuter un ou tous les tests (d'ailleurs on ne peut pas lancer des tests en faisant un clic droit depuis l'explorateur de solutions, il faut forcément utiliser un raccourci ou le test runner, radical mais étrange pour du Visual Studio...)


Test Runner

Plusieurs types de projet de test au lieu d'un seul.

Les tests d'intégration "pour les testeurs" ont été séparés des tests unitaires automatisés "pour les développeurs". Le Projet de test unitaire est à privilégier lorsqu'on veut faire du TU avec MSTest. Mais la bonne nouvelle, c'est qu'il n'y a même pas besoin d'utiliser ce type de projet pour tirer parti du test runner de Visual Studio, une bonne vieille bibliothèque de classes suffit pour peu qu'on ajoute les références adéquates.

Une plateforme unifiée pour tous les frameworks de test :

la VS Unit Test Platform. Elle fournit une API pour que des adaptateurs pour les différents frameworks de test puissent être développés (ceux de XUnit et NUnit sont déjà disponibles). Cela veut dire que virtuellement n'importe quel framework de test peut maintenant fonctionner avec le test runner de base de Visual Studio, alors qu'auparavant on devait installer soit des runners spécifiques aux frameworks, soit des runners génériques tiers (Gallio, Resharper...)


Un autre gros morceau de l'environnement de tests de VS 2012 est le framework Microsoft Fakes. Cet ambitieux composant inspiré du travail de MS Research sur Pex & Moles n'est ni plus ni moins qu'un framework d'isolation intégré à Visual Studio. Il permet de générer deux types de doublures de test : les Stubs et les Shims. Les premiers sont l'équivalent des Mocks des frameworks déjà existants et peuvent seulement remplacer des interfaces ou des classes abstraites, et les seconds sont plus lents mais peuvent mimer des classes concrètes (MS Fakes marche ici sur les plates-bandes d'outils payants comme TypeMock Isolator). Les Stubs sont recommandés pour mocker des classes de son propre projet alors que les Shims sont utiles comme doublures de classes d'assemblies tiers. On peut ainsi modifier des classes comme DateTime pour simuler une date courante différente, etc.
Au passage, on peut se demander où les concepteurs du framework sont allés chercher le terme Shim ("cale"), signe que le champ lexical de la doublure de test commence à sérieusement s'épuiser. Ceux qui avaient déjà du mal avec les fakes, stubs, mocks, dummies, etc., et on peut les comprendre, seront probablement désormais totalement perdus avec les nouvelles dénominations à la Microsoft....


Techniquement, la mise en oeuvre de ces doublures est assez inédite bien qu'un peu contraignante. Pour pouvoir utiliser MS Fakes, il faut sélectionner une référence à un assembly dans le projet de test et "Ajouter un assembly Fakes" :


Ajout d'assembly Fake

Une nouvelle DLL va alors se rajouter à nos références ; à l'intérieur, tous les Stubs et Shims possibles pour les types de l'assembly de base auront été générés et sont prêts à être utilisés dans nos tests. Malgré le surcoût initial d'ajouter l'assembly fake, cette approche est intéressante puisqu'il y a des chances que ces classes déjà présentes à la compilation soient plus performantes que des proxies générés au runtime, comme dans les frameworks d'isolation actuels.
Le setup des fakes se fait avec une syntaxe classique utilisant des lambdas. Pas de dépaysement de ce côté-là, en revanche un gros point noir : le framework ne semble pas proposer les méthodes d'assertion avancées de ses concurrents -vérifications sur le nombre d'appels d'une méthode d'un Stub, vérification simple des paramètres passés à une méthode, syntaxe fluent, etc.


Utilisation d'un Stub


Application Lifecycle Management

Du pain et de l'Agile


Je n'ai pas pu tester la partie ALM de Visual Studio 2012 par manque de serveur Team Foundation, mais le remaniement a l'air assez considérable et Scrum semble plus que jamais la méthodologie par défaut promue par Microsoft. On sent que l'équipe TFS a mis le paquet pour que Visual Studio, dans ses versions supérieures, devienne un centre complet de gestion du cycle de vie d'un projet.

Parmi les nouveautés que j'ai pu voir,

  • Une nouvelle vue Backlog plus claire (et pas juste un résultat de query TFS)
  • Un Task Board électronique présent par défaut, avec drag & drop et autres joyeusetés.
  • Un écran de planification de sprint
  • Un écran "My Work" dans Team Explorer avec les tâches de l'utilisateur
  • Un cadre pour les revues de code
  • Un système de gestion de feedback utilisateur


Task Board

En parallèle de cela, on peut noter que Microsoft a fait des efforts pour rendre ses outils plus compatibles avec la philosophie agile -notamment après les mini-vagues d'indignation qui ont eu lieu lors de la sortie des premières version beta de VS 2012. On a toujours la possibilité d'assigner les tâches à chaque développeur (façon Waterfall/micro-management) dès le sprint planning, mais ce n'est plus obligatoire ni recommandé (dans l'exemple donné dans la documentation TFS, l'équipe ne le fait pas). De même, j'ai l'impression que la référence au diagramme de Gantt qui apparaissait dans l'écran de planification de sprint a été enlevée.


Conclusion

Ce soleil qui ne voulait pas devenir une Etoile noire


Il y aurait bien d'autres choses à dire sur Visual Studio 2012, qui est un univers à lui tout seul. Comme d'habitude, Microsoft court après son écosystème - une foule de fournisseurs d'add-in tiers innovants - dont il "officialise" dans VS 2012 quelques fonctionnalités, tout en prenant bien garde à le faire assez lentement pour ne jamais rattraper tout son retard sur ces satellites, de peur d'anéantir la galaxie.
Ainsi, l'accent est surtout mis dans cette nouvelle version sur l'expérience utilisateur, l'ergonomie et la gestion de projet, et moins sur des fonctionnalités de développement "de fond" (je ne parle pas ici de la plateforme .Net et de ses langages qui ont bien évolué avec la version 4.5 du framework).

Toutefois, l'intégration d'un test runner potable fait bien progresser l'IDE dans le domaine des tests unitaires. Elle rend Visual Studio "nu" enfin utilisable de façon à peu près correcte dans un contexte de développement agile piloté par les tests - il n'en demeure pas moins que si l'on veut une bonne productivité, un plugin tiers comme Resharper me parait encore indispensable.

mardi 21 août 2012

TDD et clean Architecture, partie 3

Retour au format article pour cette 3ème et dernière partie sur Clean Architecture. Je vous recommande au passage un très bon billet qu'Uncle Bob vient de publier et qui résume un peu tout ce qu'il a produit sur cette approche jusqu'à présent.

Pour rappel, les 3 principales couches dans ce type d'architecture sont les entités, les interacteurs et les bornes. La dernière fois, nous avons implémenté les interacteurs (use cases) pour notre application FigurineShop en utilisant TDD. Cette fois-ci nous allons nous intéresser aux bornes et à ce qu'il y a derrière.

Clean Architecture

Si vous vous rappelez bien, dans l'épisode précédent nous avions des tests de collaboration pour nos interacteurs qui ressemblaient à ceci :

[Test]
public void InteracteurPeutRetournerListeFigurinesContenueDansEntrepot()
{
    var listeFigurines = new List<Figurine>
                             {
                                 new Figurine("Tintin"),
                                 new Figurine("Milou"),
                                 new Figurine("Capitaine Haddock")
                             };
    doublureEntrepot.Setup(d => d.ToutesFigurines()).Returns(listeFigurines);
 
    Assert.AreEqual(listeFigurines, interacteur.Catalogue());
}

On avait laissé les dépendances des interacteurs (entrepôts...) à l'état purement abstrait et on les avait mockées pour pouvoir tester uniquement la collaboration des interacteurs avec ces dépendances et pas ces dépendances elles-mêmes.
Jusqu'alors nous avions donc uniquement des interfaces pour ces dépendances, ce sont les bornes. Comment compléter le puzzle et implémenter les parties noires sur le schéma ci-dessus ? Et bien il suffit juste de prendre chacune de ces interfaces qui existaient pour l'instant seulement à l'état de mock et de créer son ou ses implémentations concrètes.


Mais attention, nous sommes en TDD, il faut donc commencer par un test ! Lequel ?

En l'occurrence, une chose devrait nous frapper. Nous avons précédemment écrit des tests de collaboration pour vérifier que les interacteurs communiquaient bien avec leurs dépendances en supposant que les dépendances se comportaient d'une certaine manière, et nous avons programmé nos doublures pour qu'elles agissent de cette manière. Mais nous n'avons pas encore écrit de test exigeant que les implémentations concrètes de ces dépendances se comportent réellement ainsi ! C'est ce que nous allons faire en écrivant le reflet, le symétrique des tests de collaborations mais dans l'autre sens. En d'autres termes : nous avons testé que la classe A appelle correctement une méthode de la borne abstraite B avec certains paramètres et sait gérer ses réponses. Maintenant, il faut tester que chaque implémentation de B

  • Est bien capable de répondre aux sollicitations de A avec ces paramètres,
  • Et qu'elle se comporte de telle sorte qu'elle renvoie bien ces fameuses réponses.

Tests de collaboration et de contrat

Au passage, certains auront reconnu la notion de tests de contrat définie par J. B. Rainsberger dans "Integration tests are a Scam".

Miroir (c) wreckedm 2006 http://www.sxc.hu/photo/668894

Cela parait complexe et théorique, mais en réalité c'est très facile. Pour l'instant la seule borne mockée dans les tests des interacteurs était IEntrepotFigurines, la source de données dans lequel l'interacteur allait piocher ses figurines. On avait 3 tests de collaboration :

  • Un qui testait si la méthode ToutesFigurines() de l'entrepôt était appellée par l'interacteur,
  • Un qui vérifiait que l'interacteur se comportait comme atendu quand ToutesFigurines() retournait une liste vide,
  • Et un autre qui vérifiait qu l'interacteur se comportait comme atendu quand ToutesFigurines() retournait une liste remplie.

Le premier test ne nécessite pas de "test symétrique" côté entrepôt : la méthode ToutesFigurines() n'a pas de paramètre donc on se doute que l'entrepôt va accepter les appels à cette méthode dès lors qu'il implémente le contrat (ça aurait nécessité plus de vérifications si la méthode avait eu des paramètres, et notamment des paramètres qu'on peut mettre à null). Il reste deux tests symétriques à créer :

  • Un qui teste si l'entrepôt est capable de retourner une liste vide dans certaines circonstances (symétrique du 2è test),
  • Un qui teste si l'entrepôt peut retourner une liste pleine dans d'autres circonstances (symétrique du 3è test).

Pour plus de facilité, on va ici utiliser un entrepôt qui conserve les figurines en mémoire. Dans la vraie vie, on utiliserait évidemment un entrepôt qui les conserve dans un stockage persistant (base de données)...

[TestFixture]
public class EntrepotFigurinesMémoireTest
{
    [Test]
    public void RetourneListeVideQuandPasDeFigurinesDansLEntrepot()
    {
        IEntrepotFigurines entrepotFigurines = new EntrepotMémoireFigurines();
 
        Assert.That(entrepotFigurines.ToutesFigurines(), Is.Empty);
    }
 
    [Test]
    public void RetourneListeDeFigurinesContenuesDansLEntrepot()
    {
        var tintin = new Figurine("Tintin");
        var milou = new Figurine("Milou");
        var haddock = new Figurine("Haddock");
        var listeFigurines = new List<Figurine> { tintin, milou, haddock };
        IEntrepotFigurines entrepotFigurines = new EntrepotMémoireFigurines(listeFigurines);
 
        Assert.That(entrepotFigurines.ToutesFigurines(), Is.EqualTo(listeFigurines));
    }
}

Par bonheur, ces deux tests de contrat avec l'extérieur suffisent à définir ce que doit faire l'entrepôt tout entier. L'implémentation suivante fait passer les tests :

public class EntrepotMémoireFigurines : IEntrepotFigurines
{
    protected IList<Figurine> figurines = new List<Figurine>();
 
    public EntrepotMémoireFigurines(IList<Figurine> figurines)
    {
        this.figurines = figurines;
    }
 
    public IList<Figurine> ToutesFigurines()
    {
        return figurines;
    }
}

L'autre partie des bornes se trouve côté mécanisme d'acheminement, c'est à dire interface utilisateur.
Nous allons créer un Présenteur par action utilisateur (consulter le catalogue, consulter le panier et ajouter une figurine au panier) qui va appeler l'interacteur approprié et retourner le résultat. Les présenteurs manipulent en temps normal des structures de données adaptées au mécanisme d'acheminement mais par souci de simplicité nous utiliserons directement les entités métier (ce qui fait que nos présenteurs peuvent apparaître comme de simple passe-plats, ce qu'ils sont).

Voici les tests des présenteurs :

[TestFixture]
public class PresenteurCatalogueTest
{
    [Test]
    public void PresenteurCommuniqueAvecInteracteurCatalogue()
    {
        var interacteur = new Mock<IInteracteurCatalogue>();
        var presenteur = new PresenteurCatalogue(interacteur.Object);
 
        presenteur.Catalogue();
 
        interacteur.Verify(i => i.Catalogue(), Times.Once());
    }
 
    [Test]
    public void PresenteurRetourneCatalogueQueLuiADonnéInteracteur()
    {
        var interacteur = new Mock<IInteracteurCatalogue>();
        var presenteur = new PresenteurCatalogue(interacteur.Object);
 
        var figurines = new List<Figurine> { new Figurine("Tintin"), new Figurine("Milou"), new Figurine("Haddock") };
        interacteur.Setup(i => i.Catalogue()).Returns(figurines);
 
        Assert.AreEqual(figurines, presenteur.Catalogue());
    }
}
 
[TestFixture]
public class PresenteurAjoutFigurineTest
{
    [Test]
    public void PresenteurAjoutFigurineAppelleInteracteur()
    {
        var interacteur = new Mock<IInteracteurAjoutFigurine>();
        var presenteur = new PresenteurAjoutFigurine(interacteur.Object);
 
        var figurine = new Figurine("Tintin");
        presenteur.AjouterFigurine(figurine);
 
        interacteur.Verify(i => i.AjouterFigurine(figurine), Times.Once());
    }
}
 
[TestFixture]
public class PresenteurPanierTest
{
    [Test]
    public void PresenteurAppelleInteracteurPanier()
    {
        var interacteur = new Mock<IInteracteurConsultationPanier>();
        var presenteur = new PresenteurPanier(interacteur.Object);
 
        presenteur.Update();
 
        interacteur.Verify(i => i.ConsulterPanier(), Times.Once());
    }
 
    [Test]
    public void PresenteurContientLesBonnesDonnées()
    {
        var interacteur = new Mock<IInteracteurConsultationPanier>();
        var lignesPanier = new Dictionary<Figurine, int>()
                               {
                                   {new Figurine("Tintin"), 1},
                                   {new Figurine("Milou"), 2}
                               };
        var total = 32f;
        var reponse = new ModeleReponsePanier(lignesPanier, total);
        interacteur.Setup(i => i.ConsulterPanier()).Returns(reponse);
        var presenteur = new PresenteurPanier(interacteur.Object);
 
        presenteur.Update();
 
        Assert.AreEqual(lignesPanier, presenteur.LignesPanier);
        Assert.AreEqual(total, presenteur.MontantPanier);
    }
}

Enfin, il faut une interface graphique pour afficher l'application. J'ai choisi une application console mais cela pourrait être du web ou du client lourd en réutilisant les mêmes présenteurs.

L'application console contient :

  • La méthode Main() qui sert de point de départ et de Composition Root pour assembler les différentes classes entre elles.
  • Des méthodes pour afficher le menu et les différents "écrans"

class Program
{
    static void Main(string[] args)
    {
        var panier = new PanierFigurines();
        var entrepotFigurines = new EntrepotMémoireFigurinesDémo();
        var presenteurCatalogue = new PresenteurCatalogue(new InteracteurCatalogueFigurines(entrepotFigurines));
        var presenteurAjoutFigurine = new PresenteurAjoutFigurine(new InteracteurAjoutFigurinePanier(panier));
        var presenteurPanier = new PresenteurPanier(new InteracteurConsultationPanier(panier));
 
        AfficheMenu();
        var input = string.Empty;
        while ((input = Console.ReadLine()) != "4")
        {
            switch (input)
            {
                case "1":
                    AfficheCatalogue(presenteurCatalogue);
                    break;
                case "2":
                    AffichePanier(presenteurPanier);
                    break;
                case "3":
                    SaisieFigurine(presenteurAjoutFigurine, presenteurCatalogue);
                    break;
                default:
                    Console.WriteLine("Mauvaise saisie");
                    break;
            }
            AfficheMenu();
        }
    }
 
    private static void SaisieFigurine(PresenteurAjoutFigurine presenteurAjoutFigurine, PresenteurCatalogue presenteurCatalogue)
    {
        Console.Write("Numéro de la figurine dans le catalogue ? :");
        var numero = int.Parse(Console.ReadLine());
        if (numero < 1 || numero > presenteurCatalogue.Catalogue().Count)
        {
            Console.WriteLine("Saisie invalide.");
            return;
        }
        var figurine = presenteurCatalogue.Catalogue()[numero - 1];
        presenteurAjoutFigurine.AjouterFigurine(figurine);
        Console.WriteLine("Figurine ajoutée.");
    }
 
    private static void AffichePanier(PresenteurPanier presenteurPanier)
    {
        Console.WriteLine();
        presenteurPanier.Update();
        if (presenteurPanier.LignesPanier != null)
            foreach (var lignePanier in presenteurPanier.LignesPanier)
                Console.WriteLine(lignePanier.Key.Nom + " .................... " + lignePanier.Value);
        Console.WriteLine("---------------------------------");
        Console.WriteLine("Total : " + presenteurPanier.MontantPanier + " Euros");
        Console.WriteLine();
    }
 
    private static void AfficheCatalogue(PresenteurCatalogue presenteurCatalogue)
    {
        Console.WriteLine();
        foreach (var figurine in presenteurCatalogue.Catalogue())
            Console.WriteLine(presenteurCatalogue.Catalogue().IndexOf(figurine) + 1 + "." + figurine.Nom);
        Console.WriteLine();
    }
 
    private static void AfficheMenu()
    {
        Console.WriteLine("=================================");
        Console.WriteLine("1. Consulter le catalogue");
        Console.WriteLine("2. Consulter le panier");
        Console.WriteLine("3. Ajouter une figurine au panier");
        Console.WriteLine("4. Sortir");
        Console.WriteLine("=================================");
        Console.WriteLine();
        Console.Write("Choix ? : ");
    }
}

Comme d'habitude, vous pouvez retrouver tout le code de l'application et les tests sur Github : http://github.com/infosaurus/FigurineShop

mardi 24 avril 2012

TDD et Clean Architecture, partie 2

Comme promis, voici la suite de notre série avec cette fois-ci les Interacteurs.



Les sources sont toujours disponibles ici : http://github.com/infosaurus/FigurineShop

dimanche 1 avril 2012

Code Retreat Montpellier

Le 24 Mars dernier se déroulait le premier Code Retreat à Montpellier, animé par Antoine Vernois et organisé par Julien Lafont, Vivian Pennel et Sylvain Fraïssé. L'événement avait lieu dans les locaux de l'école Epitech. Je ne vais pas rappeler les règles d'un code retreat en détail, mais il s'agit d'une rencontre où on développe en pair programming sur un problème donné - en général le Conway's Game of Life, sous forme d'itérations de 45 minutes en changeant de binôme à chaque fois. Voici mon petit retour (tardif) sur la journée.

Ce que j'ai aimé :

  • La bonne humeur générale propice aux échanges, avec des développeurs venus d'un peu partout (Montpellier mais aussi Marseille, Toulouse, etc).
  • La variété des langages présents, de Java à Python en passant par Scala, Ruby et plein d'autres.
  • Le cadre d'Epitech sympa, dans une bâtisse ancienne montpelliéraine rénovée.
  • L'efficacité d'Antoine qui avait visiblement reçu 5/5 l'unique conseil avisé de Corey Haines :)

Ce que j'ai appris :

  • Le pouvoir des contraintes. Se mettre des restrictions très pénibles (pas de if, de boucles, pas de getters/setters...) force en fait à réfléchir autrement et paradoxalement on en tire plein d'enseignements à mettre en application plus tard.
  • Un Code Retreat, ça passe très vite, aussi bien la journée que les itérations elles-mêmes. Au bout des 45 minutes, on est systématiquement frustré d'être si loin de voir notre monde en vie, surtout quand de précieuses minutes ont été passées en contingences matérielles annexes. Mais c'est le jeu, ma pauvre Lucette :)
  • Ruby, ça a l'air de roxxer. Il faut que je m'y penche de plus près.
  • Décidément, j'ai du mal à convaincre mes petits camarades que C#, c'est bien. Pourtant, C#, c'est bien ;)

Si c'était à refaire :

  • Eviter de se pointer avec un langage ou un environnement qu'on maîtrise mal. Je pense qu'il est crucial qu'un des deux membres du binôme connaisse très bien la techno utilisée pour pouvoir se concentrer uniquement sur les tests et l'émergence du design. J'ai le sentiment d'avoir passé trop de temps à cerner des détails de langages, c'est intéressant mais pas forcément dans les clous de la journée.
  • Peut-être expliquer un peu mieux au début de la journée ce qu'est TDD, ou demander aux gens de se renseigner dessus avant. J'ai l'impression qu'il y avait un taux assez haut de "totale découverte TDD" dans la salle ce qui est bien sûr enrichissant au niveau du partage, mais a peut-être généré une certaine perplexité.

Dans la catégorie "j'ai testé pour vous" :

  • Casser la cafetière du code retreat dès le matin (encore désolé Julien)... j'ai un peu fait mon boulet sur ce coup-là.
  • Le Conway's Game of Life en Python avec un binôme qui n'avait fait que du C et pas familier du développement objet. Tout ça sans if et sans boucle sinon c'est pas drôle... Ou comment expliquer le polymorphisme à quelqu'un en 5 minutes :)
  • Le ping-pong programming à 3 sans avoir le droit de se parler et avec un test runner JUnit qui crashe de temps en temps. Un grand moment... d'incompréhension, mais très instructif sur notre dépendance à la communication !


Debriefing entre deux sessions

En tout cas, très bonne expérience que ce premier code retreat, encore merci aux organisateurs et aux participants ! A quand la prochaine ? ;)

dimanche 18 mars 2012

TDD et Clean Architecture, partie 1

Après le kata, 2è vidéo sur TDD avec une petite appli d'exemple et un focus sur un type d'architecture particulier :





  • Vous pouvez retrouver la présentation d'Uncle Bob ici

lundi 2 janvier 2012

Podcasts

Ayant fait pas mal de déplacements fin 2011, j'ai occupé mon temps notamment en écoutant divers podcasts sur le développement logiciel. Pour tous ceux qui prévoient de passer du temps dans les transports cette année, je vous propose ma sélection avec mes 5 podcasts favoris en 2011 :

1. .NET Rocks! : un show très sémillant à l'américaine autour de .NET et des technologies Microsoft animé par Carl Franklin et Richard Campbell. Le podcast est de qualité professionnelle avec un très bon son, l'inconvénient étant les pubs. Bons invités (récemment Corey Haines) et toujours à la pointe de l'actualité et des sujets qui buzzent. Je le mets en premier pour la fréquence soutenue des épisodes qui fait qu'on a toujours quelque chose d'intéressant à se mettre sous la dent.

2. Visual Studio Talk Show : encore du .NET, mais comme son nom ne l'indique pas c'est un des rares podcasts francophones que j'aie trouvé sur le développement. Il est animé par les excellents Québecois Guy Barrette et Mario Cardinal. J'aime bien ce podcast pour son approche didactique et accessible, la variété des sujets et les acteurs de la communauté (québécois mais aussi français) invités.

3. Software Engineering Radio : un podcast généraliste sur le développement, les méthodes agiles... avec des invités de marque : Jurgen Appello, Lisa Crispin, Uncle Bob, Rich Hickey (créateur du langage Clojure) ont été reçus par le passé, sans compter un épisode assez mémorable avec Kent Beck.

4. Hanselminutes : le podcast de Scott Hanselmann assez orienté Web et Microsoft. J'aime bien le ton sérieux et toujours posé de Scott qui est un peu à l'inverse des compères de .NET Rocks!

5. The Java Posse : le podcast le plus populaire sur Java, avec son générique culte et sa bande de joyeux lurons assez marrants à écouter débattre en long et en large sur plein de sujets (spécial longs trajets donc). Et ça ne fait pas de mal de prendre des nouvelles de ce qui se fait du côté de Java de temps en temps ;)

Une mention également à The Agile Toolkit, Deep Fried Bytes et The Pragmatic Podcast qui valent le détour malgré une parution plus épisodique.

Je suis aussi preneur de vos suggestions de podcasts, notamment en français !


Sur ce, je vous souhaite une très bonne année 2012 :)

mardi 29 novembre 2011

Tintin, course de haies et TDD

Aujourd'hui je me lance dans un petit kata en vidéo :



N'hésitez pas à me faire part de vos commentaires... Vous pouvez aussi retrouver le code sur GitHub : http://github.com/infosaurus/KataTintin

- page 1 de 3