Véritable plaie des logiciels métiers, les développements spécifiques peuvent être évités grâce à la programmation dynamique.
Prenons une application prévue pour gérer une vidéothèque. Mettons qu’une vidéo ait deux propriétés, un titre et une date de création. Vous avez donc un écran pour afficher et modifier les deux informations de titre (une chaîne de caractères) et de création (une date). Facile ! C’est après que ça se gâte.
Ça se gâte lorsque le client vous dit qu’il aurait besoin de gérer une information supplémentaire. Cette information n’intéresse vraiment que ce client en particulier et vous n’aviez jamais entendu parler de ce besoin en écrivant le logiciel.
Trois possibilités se présentent habituellement, aucune n’étant réellement réjouissante :
Grâce à son architecture, ecsy est à l’abri de ce genre de mésaventures. Voyons un peu à quoi ressemble cette programmation dynamique.
En programmation classique orientée objet, côté code vous avez une classe Video possédant deux propriétés.
public Video
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime CreationDate { get; set; }
}
Et côté base de données vous avez la table correspondante pour le stockage.
Id | Title | CreationDate |
---|---|---|
1 | Forrest Gump | 28/10/2015 |
2 | La Ligne verte | 01/03/2000 |
3 | La Liste de Schindler | 13/03/2019 |
Le problème c’est que c’est hard codé. Vous ne pouvez pas ajouter une propriété sans modifier la classe et la table ainsi que tout le code derrière qui les utilise. Si vous ajoutez une propriété, vous faites nécessairement une montée de version.
Maintenant, imaginons que notre base de données contienne non seulement la table de stockage « Video » mais aussi une table de définition des propriétés.
ColumnName | DataType |
---|---|
Id | int |
Title | varchar(45) |
CreationDate | date |
Côté code, la classe « Video » peut alors être remplacée par un Dictionary<string, object>
avec un nombre d’entrées dépendant du nombre de lignes dans la table de définition des propriétés.
Ce qui donne après chargement des données :
result
{
{ key: "Id", Value: 1 },
{ key: "Title", Value: "Forrest Gump" },
{ key: "CreationDate", Value: 28/10/2015 }
};
Le data access layer récupère des données typées dans la base et les mappe dans le dictionnaire. Une valeur du dictionnaire est de type object mais peut être unboxée vers son type propre. Ici on peut écrire sans problème var id = (int)result["Id"];
Bien sûr c’est moins direct, le code est moins simple. Mais ça présente l’avantage d’être extensible. Un framework conçu sur ce principe permet de déclarer une nouvelle propriété dans la base :
ColumnName | DataType |
---|---|
Id | int |
Title | varchar(45) |
CreationDate | date |
Format | varchar(15) |
Et d’obtenir après chargement :
result
{
{ key: "Id", Value: 1 },
{ key: "Title", Value: "Forrest Gump" },
{ key: "CreationDate", Value: 28/10/2015 },
{ key: "Format", Value: "Blue ray" }
};
C’est un exemple volontairement simpliste, mais sur ce principe on peut rendre dynamique non seulement les propriétés mais les objets eux-mêmes, soit les tables dans la base. Et systématiser encore un peu plus en modélisant les relations entre les objets et les interactions avec l’utilisateur.
On peut donc faire exactement la même chose en utilisant uniquement des Dictionary<string, object>
qu’au moyen d’objets préalablement déclarés avec leurs propriétés.
Mais comme je le disais plus haut, avec ce système le code est moins direct, moins « beau ». On aura partout du var id = (int)result["Id"];
au lieu de var id = result.Id;
Et pour ne pas traîner des magic strings partout, il faut déclarer une flopée de constantes quelque part. Il faut vraiment que ça vaille la peine, alors que c’est si facile avec EntityFramework !
Ça vaut la peine lorsqu’il faut prévoir qu’on ne peut pas tout prévoir. Dans ce cas, on vient de voir qu’il suffit d’un framework de persistance adapté pour apporter beaucoup de souplesse au data model.
Mais il faut aussi que l’UI puisse s’adapter. La programmation dynamique doit s’appliquer également à l’interface utilisateur.
Le cas le plus élémentaire est celui de la datagrid. Il est facile de concevoir un affichage de données en tableau qui autorise la déclaration dynamique des colonnes. Il suffit de parser le dictionaire reçu pour afficher autant de colonnes que nécessaire. Si le data model change, l’interface s’adapte sans avoir à changer le code.
Ça se corse nettement dès qu’il s’agit de présenter des formulaires mais fondamentalement le procédé suit la même logique. Si le data model décrit correctement quelles sont les propriétés à afficher et les contraintes de validation, rien n’interdit de construire un composant de formulaire dynamique. Et là encore l’interface s’adapte aux changements du data model sans avoir à modifier le code.
Pour revenir à notre historiette, le client pourra ajouter la propriété « Format ». Il pourra même décider de gérer une table de référence des formats et ne plus stocker le nom du format dans sa table « Video » mais seulement l’identifiant du format, avec bien sûr une contrainte de clé étrangère entre les deux tables. Ce qui implique qu’il aura automatiquement une nouvelle entrée de menu dans son application pour gérer sa table de référence, etc.
On peut pousser la logique de programmation dynamique très loin, en incluant la gestion des droits sur les opérations CRUD et l’internationalisation mais on atteindra sans doute les limites de l’exercice en cherchant à modéliser les règles et processus métier eux-mêmes. Attendez, voyons, il faudrait un data model capable de décrire et de modifier dynamiquement les tables de description dynamique des données… arg !