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

Techniques de portage et de partage de code

Entre le simple usage des mots clefs du langage, et jusqu’à des librairies spécifiques, il y a différentes manières d’architecturer le code et de minimiser la partie spécifique à chaque plateforme. Nous allons commencer avec quelques rappels sur les bases de la programmation orientée objets, puis nous avancerons dans des concepts et des outils plus complexes.

 

Interfaces, Classes Abstraites, Classes Partielles et Méthodes d’Extension

Les Interfaces sont l’outil le plus classique, dans le monde orienté objet, pour ajouter une couche d’abstraction, en spécifiant un “contrat” de classe sans en définir l’implémentation, qui permettrait d’avoir plusieurs classes, par exemple dans des projets différents avec les mêmes champs, propriétés et méthodes. Pour en savoir plus:

L’avantage des interfaces, c’est qu’on peut en implémenter plusieurs dans une classe.

Les classes abstraites peuvent être utilisées quand il faut spécifier une implementation de base qui peut être « écrasée » par des classes qui en héritent (les classes dérivées). Par rapport à une interface, on perd le multi-héritage, mais on peut y gagner une implémetnation par défaut ! Pour en savoir plus :

Les classes partielles permettent de définir une seule classe, dans plusieurs fichiers différents. Elles sont au cœur du framework .NET et particulièrement du couple XAML/C#! Cela permet de découper la défintion d’une classe dans des fichiers séparés qui pourraient, par exemple, avoir une partie dans un projet générique, avec une autre partie dans un projet spécifique à une plateforme (à condition d’être dans le même espace de nommage). Pour en savoir plus sur les classes partielles :

Les méthodes d’extension peuvent aussi être utilisées pour découper le code de méthodes dans différents fichiers : par exemple en étendant une classe générique avec des méthodes spécifiques à chaque plateforme. LINQ est un bon exemple d’utilisation des méthodes d’extension puisque l’ajout d’un espace de nommage et d’une référence LINQ permet d’ajouter des méthodes comme Where() et Select() aux types énumérables. Pour en savoir plus sur les méthodes d’extension:

 

La Portable Library

 

La portable library permet, en function des frameworks que vous ciblez, de créer des projets “portables” utilisant le sous-ensemble de classes et de méthodes commun à toutes les cibles. La portable library est inclue par défaut dans Visual Studio 11 (sauf pour la version Express) et est disponible en tant qu’extension pour Visual Studio 2010. Les frameworks cibles supportés incluent le framework .NET, WinRT, Silverlight, Silverlight pour Windows Phone, et la Xbox 360.

 

image

 

La portable library, de par sa nature, n’inclue pas d’APIs spécifiques à une plateforme ou un OS, mais contient tous les types de base, les fonctionnalités coeur de .NET, l’accès à Internet et au web, le parsing XML… bref, ce qu’il faut pour créer des modèles objets, et éventuellement des couches d’accès aux données. Malheureusement, très peu de frameworks d’accès aux données tiers ciblent la portable library.

 

Une chose à garder en tête quand on conçoit l’architecture d’une application : un projet ciblant la portable library ne peut pas référencer de projets qui ciblent des plateformes spécifiques. Cela peut paraitre évident, pour rester indépendant justement, mais il en découle des implications importantes, notamment le fait que la portable library ne peut pas servir de couche intermédiaire entre deux composants spécifiques à une plateforme, ou bien de couche d’abstraction à des services du système d’exploitation par exemple. Pour en savoir plus à propos de la portable library :

 

Voici également une belle série d’articles par Stéphanie Hertrich qui détaille l’utilisation de cette portable library WinRT, Windows Phone et Silverlight 5 :

 

Ecrire son propre wrapper.

 

La plupart du temps on écrit du code sans penser à la portabilité… en utilisant des classes de haut niveau qui n’existent que sur une plateforme plutôt qu’une classe équivalente qui serait portable. C’est le cas, par exemple, de la classe WebClient, spécifique à Windows Phone, alors qu’il existe la classe HttpWebRequest qui est portable et qui fait la même chose (voire plus, en fait). Dans ces cas-là, 2 solutions : soit réécrire le code, soit ré-implémenter la classe manquante au-dessus de celles qui existent dans le nouveau framework… une sorte de wrapper. L’avantage est que cela permet de ne pas toucher du tout au code existant.

 

ATTENTION : n’utilisez pas de désassembleurs comme Reflector ou DotPeek, pour « récupérer » une classe dans un framework et la porter dans un autre. C’est une mauvaise pratique !

 

La compilation conditionnelle, les liens et les directives de préprocesseur

 

La “compilation conditionnelle” peut être utilisée quand on veut mélanger, dans le même fichier, du code spécifique à une plateforme, et du code générique. Ensuite il suffit d’ajouter ce fichier à tous les projets, en utilisant le bouton « ajouter un lien » dans la fenêtre d’ajout de nouveaux éléments.

 

image

 

Chacun se fera son idée de la “propreté” de cette méthode, en ce qui me concerne j’apprécie la clarté du découpage entre le code spécifique et le code générique, cela permet de voir facilement comment maximiser la réutilisation de l’une ou de l’autre.

image

[Attention] Les gestionnaires de sources ne prennent pas en compte les liens entre les fichiers (seuls les “vrais” fichiers sont commité. Il faut donc que les développeurs aient accès aux projets de toutes les plateformes s’ils lient dans leur projets des fichiers d’autres projets.

 

L’utilisation de la CTP Async pour Visual Studio

Toutes les opérations pouvant prendre plus de 50ms dans WinRT sont asynchrones dans Windows 8 Release Preview, et font usage des mots clefs async et await. Ce n’est pas le cas dans Windows Phone, mais il est possible de rajouter cette fonctionnalité et ces mots clefs dans le runtime Windows Phone à l’aide de l’Async CTP. Même s’il s’agit d’une CTP, donc non supportée, elle est pour autant suffisamment stable pour être utilisée dans une application. Cela permet de réutiliser les mêmes API et les mêmes patterns de code sur les deux plateformes, et ça peut être particulièrement utile sur tout ce qui concerne l’abstraction du hardware ou du réseau (celles-ci étant quasi systématiquement asynchrones). Vous pouvez trouver plus d’informations sur la CTP Async avec le lien suivant :

 

Attention, la CTP Async n’est pas installable sur une machine avec le framework .NET 4.5, Visual Studio 11 ou Windows 8 Release Preview !

 

L’utilisation de frameworks multi-plateformes

La plupart des développeurs font appel à des frameworks tiers pour simplifier le développement d’applications et compléter les APIs du Framework .NET : par exemple, pour parser du JSON, pour s’interfacer aux APIs des réseaux sociaux, ou pour implémenter simplement des patterns. Dans le cas où vous utilisez des frameworks tiers, assurez-vous que leurs dépendances restent dans les APIs tolérées par WinRT et les applications de type Metro (qui n’ont pas accès à l’intégralité du framework .NET 4.5).

 

XAML: Utiliser les Styles et les DataTemplates

En ce qui concerne la description des interfaces, en XAML, il faut essayer de distinguer au maximum le découpage de la page (la façon d'empiler et de situer les éléments) des éléments purement graphiques : choix des couleurs, des polices de caractères, etc. Plus cette separation sera claire, plus il sera simple de réimporter la charte graphique sur une autre plateforme tout en réarrangeant, indépendament les éléments sur la page pour utiliser l’intégralité de l’écran par exemple, ce qu’il faut absolument faire quand on change de facteur de forme et donc d’ergonomie.

 

Une dernière chose sur l’injection de dépendances, MEF et Unity

Certains outils d’injection de dépendances comme MEF et Unity auraient pu permettre de découpler certaines parties du code, en faisant encore une fois usage d’interfaces et de bouts de code plateforme spécifiquement séparés et chargés dynamiquement au démarrage. Malheureusement, le Managed Extensibility Framework (MEF) n’est pas supporté dans Windows Phone. Il est donc d’une utilité très limitée dans le cas qui nous occupe…

>> Vers la partie 4: De la théorie à la pratique: le cas de l’application Klout Kikimeter