31 votes

Interface fluide dans Delphi

Quels sont les avantages et les inconvénients de l'utilisation interfaces fluides dans Delphi ?

Les interfaces fluides sont censées améliorer la lisibilité, mais je suis un peu sceptique quant au fait d'avoir un long LOC qui contient beaucoup de méthodes enchaînées.

Y a-t-il des problèmes de compilateur ?
Y a-t-il des problèmes de débogage ?
Y a-t-il des problèmes d'exécution ou de traitement des erreurs ?

Les interfaces fluides sont utilisées, par exemple, dans les domaines suivants TStringBuilder , THTMLWriter et TGpFluentXMLBuilder .


Mis à jour :
David Heffernan a demandé quelles étaient les questions qui me préoccupaient. J'y ai réfléchi, et la question générale est la différence entre "spécifier explicitement comment faire" et "laisser le compilateur décider comment faire".

AFAICS, il n'y a aucune documentation sur la façon dont les méthodes enchaînées sont traitées par le compilateur, ni aucune spécification sur la façon dont le compilateur devrait traiter les méthodes enchaînées.

Sur cet article on peut lire comment le compilateur ajoute deux paramètres supplémentaires aux méthodes déclarées comme fonctions, et que la convention d'appel standard met trois paramètres dans le registre et les suivants sur la pile. Une "méthode de fonction fluide" avec 2 paramètres utilisera donc la pile, alors qu'une "méthode de procédure ordinaire" avec 2 paramètres n'utilise que le registre.

Nous savons également que le compilateur fait un peu de magie pour optimiser le binaire (par ex. chaîne de caractères comme résultat de la fonction , commande d'évaluation , ref to local proc ), mais parfois avec des effets secondaires surprenants pour le programmeur.

Ainsi, le fait que la gestion de la mémoire, de la pile et des registres soit plus complexe et que le compilateur puisse faire de la magie avec des effets secondaires non intentionnels me semble plutôt suspect. D'où la question.

Après avoir lu les réponses (très bonnes), ma préoccupation est fortement réduite mais ma préférence reste la même :)

23voto

gabr Points 20458

Tout le monde ne fait qu'écrire sur les points négatifs, alors soulignons les points positifs. Errr, le seul point positif - moins de (dans certains cas beaucoup moins) la saisie.

J'ai écrit GpFluentXMLBuilder juste parce que je déteste taper des tonnes de code en créant des documents XML. Rien de plus et rien de moins.

Le point positif des interfaces fluides est que vous n'êtes pas obligé de les utiliser de manière fluide si vous détestez l'idiome. Elles sont tout à fait utilisables de manière traditionnelle.

EDIT : Un point pour le point de vue "brièveté et lisibilité".

Je déboguais un vieux code et je suis tombé sur ça :

fdsUnreportedMessages.Add(CreateFluentXml
  .UTF8
  .AddChild('LogEntry')
    .AddChild('Time', Now)
    .AddSibling('Severity', msg.MsgID)
    .AddSibling('Message', msg.MsgData.AsString)
  .AsString);

J'ai su immédiatement ce que faisait le code. Si, cependant, le code ressemblerait à ceci (et je ne prétends pas que cela compile, je l'ai juste jeté ensemble pour la démonstration) :

var
  xmlData: IXMLNode;
  xmlDoc : IXMLDocument;
  xmlKey : IXMLNode;
  xmlRoot: IXMLNode;

  xmlDoc := CreateXMLDoc;
  xmlDoc.AppendChild(xmlDoc.CreateProcessingInstruction('xml', 
    'version="1.0" encoding="UTF-8"'));
  xmlRoot := xmlDoc.CreateElement('LogEntry');
  xmlDoc.AppendChild(xmlRoot);
  xmlKey := xmlDoc.CreateElement('Time');
  xmlDoc.AppendChild(xmlKey);
  xmlData := xmlDoc.CreateTextNode(FormatDateTime(
    'yyyy-mm-dd"T"hh":"mm":"ss.zzz', Now));
  xmlKey.AppendChild(xmlData);
  xmlKey := xmlDoc.CreateElement('Severity');
  xmlDoc.AppendChild(xmlKey);
  xmlData := xmlDoc.CreateTextNode(IntToStr(msg.MsgID));
  xmlKey.AppendChild(xmlData);
  xmlKey := xmlDoc.CreateElement('Message');
  xmlDoc.AppendChild(xmlKey);
  xmlData := xmlDoc.CreateTextNode(msg.MsgData.AsString);
  xmlKey.AppendChild(xmlData);
  fdsUnreportedMessages.Add(xmlKey.XML);

Il me faudrait un certain temps (et une tasse de café) pour comprendre ce qu'il fait.

EDIT2 :

Eric Grange a soulevé un point parfaitement valable dans les commentaires. En réalité, il faudrait utiliser un wrapper XML et non DOM directement. Par exemple, en utilisant OmniXMLUtils à partir de l'application OmniXML le code ressemblerait à cela :

var
  xmlDoc: IXMLDocument;
  xmlLog: IXMLNode;

  xmlDoc := CreateXMLDoc;
  xmlDoc.AppendChild(xmlDoc.CreateProcessingInstruction(
    'xml', 'version="1.0" encoding="UTF-8"'));
  xmlLog := EnsureNode(xmlDoc, 'LogEntry');
  SetNodeTextDateTime(xmlLog, 'Time', Now);
  SetNodeTextInt(xmlLog, 'Severity', msg.MsgID);
  SetNodeText(xmlLog, 'Message', msg.MsgData.AsString);
  fdsUnreportedMessages.Add(XMLSaveToString(xmlDoc));

Pourtant, je préfère la version fluide. (Et je n'utilise jamais les formateurs de code).

18voto

Eric Grange Points 3397

Problèmes de compilateur :

Si vous utilisez des interfaces (plutôt que des objets), chaque appel dans la chaîne entraînera un surcoût de comptage des références, même si la même interface est retournée à chaque fois, le compilateur n'a aucun moyen de le savoir. Vous allez donc générer un code plus gros, avec une pile plus complexe.

Problèmes de débogage :

La chaîne d'appel étant considérée comme une instruction unique, vous ne pouvez pas effectuer de step ou de breakpoint sur les étapes intermédiaires. Vous ne pouvez pas non plus évaluer l'état aux étapes intermédiaires. La seule façon de déboguer les étapes intermédiaires est de tracer dans la vue asm. La pile d'appels dans le débogueur ne sera pas claire non plus, si les mêmes méthodes se produisent plusieurs fois dans la chaîne fluide.

Problèmes d'exécution :

Lorsque vous utilisez des interfaces pour la chaîne (plutôt que des objets), vous devez payer pour la surcharge de comptage des références, ainsi qu'un cadre d'exception plus complexe. Vous ne pouvez pas avoir de constructions try..finally dans une chaîne, donc aucune garantie de fermeture de ce qui a été ouvert dans une chaîne fluente f.i.

Problèmes de journalisation des débogages/erreurs :

Les exceptions et leur trace de pile verront la chaîne comme une seule instruction. Ainsi, si vous vous êtes planté dans .DoSomething, et que la chaîne d'appel comporte plusieurs appels .DoSomething, vous ne saurez pas lequel a causé le problème.

Problèmes de formatage du code :

AFAICT, aucun des formateurs de code existants ne mettra correctement en page une chaîne d'appel fluide, donc seul le formatage manuel peut garder une chaîne d'appel fluide lisible. Si un formateur automatique est utilisé, il transformera généralement une chaîne en un désordre de lisibilité.

7voto

Cosmin Prund Points 21063

Are there any compiler issues?

Non.

Are there any debugging issues?

Oui. Comme tous les appels de méthodes enchaînés sont considérés comme une seule expression, même si vous les écrivez sur plusieurs lignes comme dans l'exemple Wikipedia que vous avez cité, cela pose un problème lors du débogage car vous ne pouvez pas les parcourir en une seule étape.

Are there any runtime/error handling issues?

Modifié : Voici une application de console de test que j'ai écrite pour tester la charge d'exécution réelle de l'utilisation des interfaces fluentes. J'ai assigné 6 propriétés pour chaque itération (en fait les 2 mêmes valeurs 3 fois chacune). Les conclusions sont les suivantes :

  • Avec les interfaces : augmentation de 70% du temps d'exécution, en fonction du nombre de propriétés définies. En définissant seulement deux propriétés, l'augmentation était plus faible.
  • Avec des objets : L'utilisation d'interfaces fluides a été plus rapide
  • Je n'ai pas testé les dossiers. Ça ne peut pas bien fonctionner avec des disques !

Personnellement, ces "interfaces fluides" ne me dérangent pas. Je n'avais jamais entendu ce nom auparavant, mais je les ai utilisées, notamment dans du code qui remplit une liste à partir de code. (Un peu comme l'exemple XML que vous avez posté). Je ne pense pas qu'elles soient difficiles à lire, surtout si l'on est familier avec ce type de codage ET que les noms des méthodes sont significatifs. Quant à la longue ligne de code, regardez l'exemple Wikipedia : Vous n'avez pas besoin de tout mettre sur une seule ligne de code.

Je me rappelle clairement les avoir utilisés avec Turbo Pascal pour initialiser les écrans, ce qui est probablement la raison pour laquelle ils ne me dérangent pas et que je les utilise parfois. Malheureusement, Google me laisse tomber, je ne trouve aucun exemple de code pour l'ancien code TP.

3voto

Daniel Maurić Points 2170

Je m'interroge sur l'intérêt d'utiliser des "interfaces fluides".

D'après ce que je vois, le but est de vous permettre d'éviter de devoir déclarer une variable. Donc, le même avantage que la redoutable instruction With apporte, avec un ensemble différent de problèmes (voir les autres réponses).

Honnêtement, je n'ai jamais compris la motivation d'utiliser l'instruction With, et je ne comprends pas non plus la motivation d'utiliser des interfaces fluides. Je veux dire, est-ce si difficile de définir une variable ? Tout ce charabia juste pour permettre la paresse.

Je dirais qu'au lieu d'améliorer la lisibilité, ce qui semble être le cas à première vue puisqu'il faut moins taper/lire, cela l'obscurcit en fait.

Donc, une fois de plus, je demande pourquoi vous voulez utiliser des interfaces fluides en premier lieu ?

Il a été inventé par Martin Fowler, donc ça doit être cool ? Non, je n'y crois pas.

2voto

C'est une sorte de notation "écrire une fois, lire jamais" qui n'est pas facile à comprendre sans passer par la documentation de toutes les méthodes concernées. De plus, cette notation n'est pas compatible avec les propriétés Delphi et C# - si vous avez besoin de définir des propriétés, vous devez revenir à l'utilisation de notations communes car vous ne pouvez pas enchaîner les affectations de propriétés. des notations courantes, car vous ne pouvez pas enchaîner les affectations de propriétés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X