Cette (longue) série d’articles est découpée en plusieurs billets sur ce blog, mais disponible également dans un seul document ici:

http://sdrv.ms/K4BXdN

Le Processus de Portage.

Avant toute chose: Travaillez votre expérience utilisateur.

Comme dit dans la partie précédente : avant de plonger dans le code, travaillez votre expérience utilisateur : que ce soit simplement avec un papier et un crayon, ou bien avec Sketchflow, ou PowerPoint, ou n’importe quel autre outil de mockup. Ce travail vous fournira une ligne directrice pendant tout le reste du travail de portage.

 

Étape 1 : Savoir regarder son code, froidement

[Dette technique] http://en.wikipedia.org/wiki/Technical_debt

L’idée de dette technique est la suivante : le plus vous « cassez » votre code en y appliquant des corrections rapides et pas forcément très propres, le plus vous aurez de problèmes à résoudre pour faire évoluer votre code. L’ensemble de ces problèmes représente votre dette technique.

La première étape quand vous portez votre application sur une nouvelle plateforme, c’est de revoir votre code. Cette revue de code est souvent prise un peu trop à la légère au profit de résultats rapide, et aussi parce qu’en tant que développeur on a tendance à croire qu’on connait son code par cœur. En fait, durant tout le développement et particulièrement durant le débuggage et la phase de tests pré-certification, nous cassons tous les patterns pour résoudre des « petits bugs », tout en accroissant notre dette technique. Tous ces petits fix ne sont pas forcément très lisibles, ni frais dans notre mémoire. Enfin, même si vous avez déjà revu votre code dans le passé, le revoir une nouvelle fois avec l’idée du portage en tête vous en donnera surement une lecture différente.

 

Le but de cette revue de code est donc de rafraichir votre mémoire sur les différents composants de votre application, les couches de votre architecture, et à quel point ils sont dépendants les uns des autres. A la fin de cette revue de code, vous aurez probablement en tête un plan de refactoring du code pour préparer le portage... Une étape que vous auriez surement négligée si vous n’aviez pas fait cette revue !

 

A ce point du processus, il est facile de céder à la tentation de copier vos fichiers sources dans un nouveau projet d’application Metro, et de commenter tout ce qui ne compile pas, pour avoir un début de prototype en 10 minutes. C’est une approche pragmatique mais qui risque d’être pénalisante pour votre utilisateur, car elle vous pousse à conserver une expérience existante parfois inadaptée, et vous risquez d’augmenter votre dette technique alors que le but de la revue de code précédente était justement de la diminuer !

Que faut-il chercher pendant cette revue de code “spéciale portabilité”?

Pour chaque projet de votre solution, vous devez lister les références que vous utilisez et à quel point elles sont portables. Si vous utilisez des frameworks tiers, il faut savoir s’il en existe des versions portables (soit à travers la portable library, ou ciblant spécifiquement chaque plateforme). Le résultat de cette investigation sera surement que cela existe, étant donné que c’est le but même de l’existence de ces frameworks… Mais parfois vous constaterez que les fonctionnalités ne sont pas les mêmes d’une version à une autre, ou bien qu’elles ne sont tout simplement pas au même niveau de maturité ou de stabilité. A vous de voir dans ce cas, si vous souhaitez vous attaquer plutôt au portage de l’existent ou tenter de remplacer par un framework concurrent. En tout état de cause, c’est plus facile si le code source existe, et dans ce cas-là, pourquoi ne pas contribuer à la communauté ?

 

En ce qui me concerne, j’utilise surtout Json.Net et parfois MVVM Light, qui sont tous deux très portables.

 

Pour chacun des fichiers sources, essayez de nettoyer vos “using” par exemple en utilisant la fonctionnalité de Visual Studio qui permet de les réorganiser. Avoir la liste la plus courte possible de « using » permet de comprendre plus facilement les dépendances de vos fichiers et de vos références.

 

image

 

Pour chaque appel à une fonctionnalité ou une API spécifique de la plateforme, voyez si l’équivalent existe sur l’autre plateforme. Il y a quelques billets dans la communauté qui relatent l’expérience des développeurs sur le sujet :

 

 

Le dernier lien pointe vers une série de billets de mon collègue Jared Bienz qui promet d’être particulièrement bien détaillée.

 

Si vous disposez d’une version de Visual Studio qui embarque les outils d’architecture (Premium ou Ultimate), c’est peut-être aussi l’occasion de s’en servir : ils sont capables de générer des graphes qui permettent de voir tout de suite à quel points vous avez cassé les patterns que vous avez utilisé, et les dépendances entre vos composants.

 

image

 

Voici un exemple de ce que j’obtiens avec une application cross-platform en cours d’écriture :

 

image

 

On voit à la fois dans l’application elle-même (FoursquareTracker) le pattern MVVM et la séparation des namespaces (qui correspond à peu près aux assemblies) et les dépendances à des namespaces externes.

 

Cette étape vous permettra d’avoir une idée à des dépendances de vos fichiers entre eux, et vous mènera naturellement à l’étape 2, qui consiste à ré-architecturer le code en modules portables individuellement. Une fois que ces modules seront découpés, il ne restera plus qu’à choisir la bonne stratégie de portage pour chacun.

 

Etape 2 : Séparer les composants (ou modules) du code et choisir la bonne technique de portage pour chacun

Pour le reste de l’article nous allons considérer qu’un composant (ou module) est un bout de code faiblement lié au reste de l’application. Si vous n’avez pas encore réussi à isoler vos composants (ce que j’appelle du code « spaghetti »), il faut revenir à l’étape de revue de code et de refactoring. En tout état de cause, démarrer un portage sur du code spaghetti c’est s’assurer à terme de perdre du temps et de passer un assez mauvais moment à fabriquer quelque chose de bancal. C’est donc le dernier moment pour faire votre nettoyage de printemps, et ça commence avec le choix de bons design patterns…

 

Un design pattern est une solution prouvée à un problème récurrent. C’est le résultat de travail de développeurs qui ont essuyés les plâtres avant vous. La plupart des designs patterns ont un objectif principal qui est la « séparation des problèmes » (separation of concerns). En d’autres mots, faire en sorte que le code soit découpé en unités fonctionnelles faiblement couplées pouvant évoluer indépendamment les unes des autres. Utiliser un design pattern permet souvent d’améliorer la testabilité du code, sa lisibilité, et même, sa portabilité ! Il faut toutefois faire attention à ne pas surutiliser les patterns, un défaut classique de développeur chevronné qui oublie que si un jour ce code est repris par quelqu’un de moins sénior, il sera surement moins lisible car il sera plus difficile de comprendre le rôle de chacun des composants… amenant donc l’effet inverse que celui escompté : des problèmes de maintenabilité, de performances, et d’évolutivité (difficile pour un développeur novice de faire évoluer correctement un code qu’il ne comprend pas !)…

 

Voici quelques patterns que vous devriez avoir en tête en pensant « portabilité »

  • Le pattern MVVM, ou n’importe quel autre pattern de séparation entre les vues et le code métier de l’application, pour tout ce qui est interface graphique.
  • Les patterns Adapter, Bridge et Factory pour tout ce qui est séparation du code spécifique la plateforme et au matériel:
    • Les services du système d’exploitation
    • L’accès aux capteurs et au matériel d’une manière générale.

 

En plus des design patterns, essayez de nettoyer votre code au maximum en le factorisant (ce que nous avons tendance à faire naturellement en C# mais pas forcément en XAML : hors les DataTemplates et les Styles sont là pour ça, et s’appliquer à soigner son XAML facilitera le portage des vues, qui est sans doute la plus grosse partie du travail à réaliser, comme on le verra par la suite).

 

Il y a différents moyens de séparer les composants dans votre code. La manière que je qualifie « d’horizontale » consiste à séparer le code sous forme de couches métiers, par exemple entre les modèles objets et les objets contenant les données à afficher. Ces composants s’étagent en général de bas en haut dans un diagramme d’architecture. La manière « verticale » de séparer les composants consiste à essayer d’isoler plutôt les fonctionnalités de l’application : par exemple, la fonctionnalité « gestion des favoris » ou la fonctionnalité « comparaison des scores ».

 

Une fois que vous avez séparé vos composants horizontalement et verticalement vous obtenez une sorte de matrice de composants. Cette matrice de composant va vous permettre d’examiner chacun des composants individuellement pour choisir la meilleure technique de portage.

Etape 3 : Passer à l’action : découper son code sous forme de solutions/projets/namespaces pour maximiser la portabilité, et réparer.

Dans cette étape, pas besoin de gants : on casse avant de reconstruire. Il faut prendre les morceaux de codes (qu’ils soient dans des fichiers séparés ou pas, et peu importe l’architecture actuelle du projet) et les réordonner, en fonction des stratégies de portage que vous adopterez pour chacun des composants… pas la peine, donc d’en écrire plus pour le moment. Voyons ces techniques !

 

>> Vers la partie 3: Techniques d’adaptation et de partage de code