lundi 10 décembre 2007

Surcharger une méthode d'extension

Les surcharges de méthodes d'extension

Préambule

L'article a nécessité une correction post-publication le 2 décembre. Il m'a paru important de conserver le contenu initial de ce dernier par transparence vis-à-vis des premiers lecteurs mais aussi parce que l'erreur commise a un intérêt pédagogique. Un chapitre « Mise à jour » a donc été ajouté en fin d'article, le reste n'ayant pas été modifié.


Rappel sur les méthodes d'extension
Pour définir une méthode d'extension, il suffit de créer une classe statique. Ensuite, le premier paramètre de chaque méthode sera « balisé » par le mot clé « this » afin de désigner le type de l'objet que l'on souhaite « étendre ».Prenons un exemple. Soit un objet « Personne » ayant pour propriétés : un prénom, un nom et un âge.
public class Personne
{
public string Nom { get; set; }
public string Prenom { get; set; }
public int Age { get; set; }
}Si vos objets sont alimentés par différentes sources, vous vous apercevrez que le nom est parfois en majuscule, parfois en minuscule, ce qui vous posera problème par exemple pour écrire de façon standard un courrier sous Word.
List personnes = new List();
personnes.Add(new Personne(){Nom="DURAND",Prenom="Cyril",Age=21});
personnes.Add(new Personne() { Nom = "Kempé", Prenom = "Laurent", Age = 36 });
personnes.Add(new Personne() { Nom = "MELANTOIS", Prenom = "Frédéric", Age = 38 });
personnes.Add(new Personne() { Nom = "Perfetti", Prenom = "Michel", Age = 30 });Pour nos documents, nous aimerions bien avoir une méthode « PrenomNom ()» dans la classe « Personne », permettant d'avoir le prénom puis le nom en majuscules. Mais dans bien des cas, vous n'aurez pas la possibilité de réécrire cette classe. Les méthodes d'extension viennent alors à notre secours :
namespace EspaceDeNom1
{
public static class ExtensionPersonne
{
//prénom + Nom en majuscule
//[//System.Runtime.CompilerServices.Extension]
public static string PrenomNom(this Personne p)
{
StringBuilder s = new StringBuilder(p.Prenom);
s.Append(" ");
s.Append(p.Nom.ToUpper());
return s.ToString();
}
}
}Veuillez noter que le compilateur C# refuse l'utilisation de l'attribut « System.Runtime.CompilerServices.ExtensionAttribute » contrairement à Visual Basic. La seule façon de créer une méthode d'extension est d'ajouter le mot clé « this ». Sous le capot, comme nous avons pu le voir dans un précédent article, le « this » est retranscrit en attribut sur la classe et la méthode statique au niveau de l'IL (Intermediate Language).Le compilateur permet un usage aisé de la méthode :
foreach (string identite in from p in personnes select p.PrenomNom())
{
Console.WriteLine(identite);
}Ce qui nous donne le résultat suivant :

Nous pouvons constater qu'un des noms comporte une majuscule avec accent, ce qui ne nous convient pas. Imaginons que nous ne sommes pas en possession des sources de la méthode d'extension ou que celle-ci se trouve dans une assembly déjà déployée. Nous avons très largement employé la méthode d'extension « PrenomNom() » un peu partout dans notre projet. Celle-ci satisfait nombre de vos collègues mais pas vous dans votre propre utilisation. Vous pouvez alors soit modifier son utilisation par un remplacement systématique par une autre méthode soit « surcharger » la méthode d'extension. Cette dernière solution est bien évidemment bien moins lourde !
Surcharger une méthode d'extension
La surcharge d'une méthode d'extension est possible sous certaines conditions. Celle-ci doit se trouver dans la même assembly et le même espace de nom que l'appel à la méthode d'extension. Si ces conditions ne sont pas remplies, le compilateur renverra une erreur signifiant un appel ambigu de méthodes d'extension. Ecrivons rapidement une méthode d'extension (la performance extrême dans les exemples n'est pas l'objet de l'article)
public static class ExtensionPersonne
{
internal static string UpperToStandardUpper(this string chaine)
{
string accent = "ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜ";
string sansAccent = "AAAAAACEEEEIIIINOOOOOOUUUU";
char[] tableauSansAccent = sansAccent.ToCharArray();
char[] tableauAccent = accent.ToCharArray();
for (int i = 0; i < accent.Length; i++)
{
chaine = chaine.Replace(tableauAccent[i].ToString(), tableauSansAccent[i].ToString());
}
return chaine;
}

public static string PrenomNom(this Personne p)
{
StringBuilder s = new StringBuilder(p.Prenom);
s.Append(" ");
s.Append(p.Nom.ToUpper().UpperToStandardUpper());
return s.ToString();
}
}Si nous ajoutons ce code dans le même espace de nom que l'appel, une surcharge de méthode d'extension s'opère. De sorte que la requête suivante :
foreach (string identite in from p in personnes select p.PrenomNom())
{
Console.WriteLine(identite);
}renvoie :

Nous venons de réaliser avec succès une surcharge de méthode d'extension. Cet aspect nous apparaît bien pratique. Toutefois, sachez que bien évidemment vous pouvez passer outre par l'invocation direct des méthodes :
foreach (string identite in from p in personnes select EspaceDeNom1.ExtensionPersonne.PrenomNom(p))
ou
foreach (string identite in from p in personnes select ConsoleApplication1.ExtensionPersonne.PrenomNom(p))
Résoudre les éventuels conflits
Les méthodes d'extension sont très séduisantes mais peuvent générer des conflits. En effet, il suffit d'utiliser deux espaces de noms différents comportant une signature de méthode d'extension identique. C'est alors que le compilateur génère une erreur d'appel ambigu de méthodes d'extension. Comment éviter au maximum ces conflits ? Comme le suggère la documentation MSDN, il faut rassembler ses méthodes d'extension sous un même espace de nom bien évocateur : « Extensions ». Si ceci est une bonne pratique, car elle permet de localiser très rapidement l'ensemble de vos méthodes d'extension, cela n'évitera pas les conflits si un éditeur de logiciel a créé une même signature que vous. Une solution peut être de préfixer chacun de vos noms de méthode d'extension. C'est à vous de bien penser au nom que vous allez donner à votre méthode. Il existe une solution pour limiter l'impact de vos méthodes d'extension, c'est de changer la portée de « public » à « internal ». Nous l'avons vu dans un précédent exemple avec la méthode « UpperToStandardUpper ». Sa portée est donc limitée à l'assembly de sorte que les utilisateurs de cette dernière n'ont pas accès à cette méthode et n'auront donc pas de conflit. C'est une pratique à généraliser dès les premières heures d'utilisation de c# 3.0 ou de Visual Basic 9
Un exemple de surcharge de méthodes d'extension de Linq
Dans certains cas, il peut être intéressant de surcharger une méthode d'extension. Si vous lisez cet article, vous avez sans doute déjà utilisé Linq et en particulier Linq to SQL. Pour ceux d'entre vous qui l'utiliseront, se posera une problématique concernant la couche d'accès aux données que vous aurez construite. En effet, si nous proposons une méthode renvoyant un IEnumerable dans notre DAL
public IEnumerable GetPersonnes()
{
DataClasses1DataContext t = new DataClasses1DataContext(_myDatabaseConnectionString);
var c = from h in t.PERSONNEs select h;
return c;
}Vous ne pourrez empêcher personne d'écrire dans la couche de présentation ceci :
IQueryable q = (IQueryable) MyDAL.GetPersonnes();
var r = from s in q where s.Personne_Age > 30 select s;ce qui aura pour incidence de créer un nouvel accès à la base de données par l'usage du IQueryable. Pour éviter, cette problématique, il faut que notre couche d'accès ne renvoie que des IEnumerable aucunement IQueryable. Nous pouvons réitérer de manière à ne plus avoir d'IQueryable de la façon suivante :
public IEnumerable GetPersonnes()
{
DataClasses1DataContext t = new DataClasses1DataContext(_myDatabaseConnectionString);
var c = from h in t.PERSONNEs select h;
return from i in c.AsEnumerable() select i;
}Mais si nous devons appliquer ces changements à chaque méthode de notre couche d'accès, ce travail devient fastidieux et nous risquons d'en oublier. La surcharge de méthodes d'extension peut venir à notre secours dans ce cas, en surchargeant la méthode d'extension « Select » de Linq To Objects. Voici le code de la surcharge :
internal static class ExtensionLinq
{
internal static IEnumerable Select(this IEnumerable source,
Func selector)
{
IEnumerable sourceEnum = source;
foreach (TSource s in sourceEnum)
yield return selector.Invoke(s);
}
}Cette surcharge permet de ne plus avoir à ce soucier dans la couche de présentation des manipulations telles que :
IQueryable q = (IQueryable) MyDAL.GetPersonnes();
var r = from s in q where s.Personne_Age > 30 select s;Elles deviennent impossibles. De sorte, que les requêtes Linq en couche de présentation ne se font qu'en mémoire. Vous aurez remarqué que j'ai pris soin d'écrire ma surcharge avec la portée « internal » de sorte qu'il n'y ait pas d'erreur d'appel ambigu de méthodes d'extension dans ma couche de présentation en faisant usage de Linq.
Mise à jour du 2 décembre
Je tiens vivement à remercier Flavien pour m'avoir notifié une ligne de code inutile. En ont découlé des discussions très instructives avec Flavien CHARLON et Matthieu MEZIL qui m'ont amené à retester différents cas de figure et à montrer que la surcharge du « Select » pour « Linq To SQL » n'était pas une solution satisfaisante.Rappelons le code proposé :
internal static class ExtensionLinq
{
internal static IEnumerable Select(this IEnumerable source,
Func selector)
{
IEnumerable sourceEnum = source;
foreach (TSource s in sourceEnum)
yield return selector.Invoke(s);
}
}la ligne « IEnumerable sourceEnum = source; » est complètement inutile car le « yield return » fabrique sous le capot un objet privé implémentant IEnumerable et non IQueryable. Voici le code que j'aurai dû écrire :
internal static IEnumerable Select(this IEnumerable source,
Func selector)
{
foreach (TSource s in source)
yield return selector.Invoke(s);
}On pourrait être satisfait mais le Select ne fonctionne pas comme on le souhaiterait. En effet, si au lieu de renvoyer toutes les colonnes de la table Personne, nous sélectionnons uniquement le nom par exemple :
var c = from h in dt.PERSONNEs where h.Personne_Age > 30 select h.Personne_Nom;La requête effectuée vers la base de donnée renverra toutes les colonnes avant que n'opère le « Select » que nous avons écrit. Cette solution n'est donc pas satisfaisante. Si l'on souhaite que la base de données ne renvoie que les colonnes que l'on a réellement besoin, il faut faire appel à la méthode d'extension « Select » de « System.Linq.Queryable » :
internal static IEnumerable Select(this IQueryable source,
System.Linq.Expressions.Expression
{
IQueryable iq = Queryable.Select(source, selector);
foreach (TResult s in iq)
yield return s;
}Si vous utilisez le Debugger ou le système de log du « DataContext », vous pourrez constater que cette surcharge répond désormais à notre exigence.Toutefois, les discutions avec mes camarades m'ont amené à effectuer de nombreux tests. J'ai alors pu constater que cette surcharge ne fonctionnait pas dans certains cas. Par exemple, si vous écrivez une requête du style « select * from matable where condition », le compilateur ne génèrera que « Table.Where(condition) » car le « Select » est inutile car toutes les colonnes sont demandées. On peut donc se féliciter d'un tel comportement pour ce qui est de la performance. Mais, la surcharge que je vous propose, ne fonctionne donc pas dans tous les cas. Je vous recommande donc de ne pas l'employer dans votre couche d'accès. Vous pouvez écrire une méthode d'extension qui empêchera de pouvoir requêter de nouveau via « Linq to SQL » :
internal static IEnumerable ToLinqToObject(this IQueryable source)
{
foreach (TSource s in source)
yield return s;
}Son utilisation se fera comme suit :
public IEnumerable GetPersonnes()
{
DataClasses1DataContext t = new DataClasses1DataContext(myDatabaseConnectionString);
var c = from h in t.PERSONNEs where h.Personne_Age > 30 select h;
return c.ToLinqToObject();//return from i in c.AsEnumerable() select i;
}

source : http://www.techheadbrothers.com/

dimanche 2 décembre 2007

Segmentation du marché et Personas

La segmentation du marché et les personas sont deux outils différents ayant chacun leurs propres objectifs. On écoute souvent dire : « je travaille déjà avec des personas, parce que je segmente mon marché ». En réalité, alors que la segmentation de votre marché vous aide à cibler des clients potentiels qualifiés, les personas, eux, vous aident à satisfaire ces clients potentiels. Votre objectif, en tant qu’éditeur de site web, est donc de combiner ces deux outils marketing pour, d’une part, générer la demande et d’autre part, satisfaire cette demande.
La segmentation du marché
La segmentation du marché consiste à trouver les meilleures façons de cibler et de délivrer des messages marketing qui créeront la demande. Une population de clients potentiels est segmentée en groupes qui, en raison de leurs différences, sont susceptibles de répondre à toute une variété de messages.
En raison de son objectif d’attirer l’attention du consommateur, de le persuader et de créer la demande, la segmentation du marché est basée sur des variables telles que l’âge, le genre, le revenu, le niveau d’éducation, les hobbies, la situation géographique, le style de vie, la personnalité psychologique... Cet outil est indispensable car il vous aide à définir comment parler efficacement aux différents groupes qui composent votre marché.
Les personas
Les personas, quant à eux, concernent la manière dont vos clients potentiels vont répondre à votre message. Les personas sont des segments définis principalement par les objectifs et les comportements de chaque groupe d’individus, car ces attributs sont les éléments les plus importants de la prise de décision. Les objectifs révèlent pourquoi les gens viennent sur votre site et ce qu’ils souhaitent y trouver, et les comportements montrent comment ils envisagent d’accomplir d’atteindre leurs objectifs. Par exemple, pour un site d’achat de biens immobiliers, si vous avez un segment dont l’objectif est de trouver un appartement, vous savez quels contenu et fonctionnalités proposer pour aider ce segment de population. Si vous connaissez le comportement de ce segment, comme, par exemple, s’il préfère principalement être contacté parce qu’il a trouvé l’appartement qui lui convenait sur votre site, vous pouvez prendre des décisions efficaces sur la manière de créer et structurer ce type de fonctionnalités et de contenu.
Synergie des deux outils
La segmentation de marché répond aux questions « Qui » et « Quoi » : qui ciblez-vous ? Quels produits ou services désirent-ils ? Que sont-ils susceptibles d’acheter et à quel prix ? Quels sont les outils marketing qui les atteindront le mieux ?... Une fois que vous avez répondu au « Qui » et au « Quoi », les personas vous aident à répondre aux questions du « Pourquoi » et du « Comment » : pourquoi ce produit ou service répond-il au besoin de la cible et pourquoi va-t-elle l’utiliser ? Comment va-t-on structurer le site pour qu’il satisfasse au mieux les besoins de la cible ? Comment être certain que le site réponde aux exigences de la cible tout en obtenant le chiffre d’affaires escompté ? Ces deux méthodes sont essentielles pour créer et implémenter une stratégie efficace pour votre site web, et sont plus performantes, naturellement, si elles sont employées ensemble. Plus vous incorporerez les personas suffisamment tôt dans le processus de segmentation de votre marché, plus le profil obtenu de vos clients potentiels sera complet.
En conclusion
L‘utilisation combinée de la segmentation du marché et des personas vous fournit une meilleure compréhension de vos clients, incluant la manière de les faire venir jusqu’à vous, et la manière de les satisfaire pour qu’ils puissent à nouveau revenir sur votre site. Ensemble, ces deux techniques sont votre meilleur atout pour réussir avec votre site web.