Récemment, j'ai développé ma propre API et avec cet intérêt investi dans la conception d'API, je me suis vivement intéressé à la façon d'améliorer ma conception d'API.
Un aspect qui a été soulevé à plusieurs reprises est (non pas par les utilisateurs de mon API mais dans ma discussion d'observation sur le sujet): il faut savoir simplement en regardant le code appelant l'API ce qu'il fait .
Par exemple, voyez cette discussion sur GitHub pour le dépôt de discours , ça va quelque chose comme:
foo.update_pinned(true, true);
En regardant simplement le code (sans connaître les noms des paramètres, la documentation, etc.), on ne peut pas deviner ce qu'il va faire - que signifie le deuxième argument? L'amélioration suggérée est d'avoir quelque chose comme:
foo.pin()
foo.unpin()
foo.pin_globally()
Et cela clarifie les choses (le deuxième argument était de savoir s'il fallait épingler foo globalement, je suppose), et je suis d'accord dans ce cas, la dernière serait certainement une amélioration.
Cependant, je pense qu'il peut y avoir des cas où des méthodes pour définir un état différent mais logiquement lié seraient mieux exposées en tant qu'appel de méthode plutôt qu'en tant qu'appels séparés, même si vous ne savez pas ce qu'il fait simplement en regardant le code . (Il faudrait donc recourir à la recherche des noms de paramètres et de la documentation pour le savoir - ce que personnellement je ferais toujours, peu importe si je ne suis pas familier avec une API).
Par exemple, j'expose une méthode SetVisibility(bool, string, bool)
sur un FalconPeer et je reconnais simplement en regardant la ligne:
falconPeer.SetVisibility(true, "aerw3", true);
Vous n'auriez aucune idée de ce qu'il fait. Il définit 3 valeurs différentes qui contrôlent la "visibilité" du falconPeer
dans le sens logique: accepter les demandes de jointure, uniquement avec mot de passe et répondre aux demandes de découverte. Diviser cela en 3 appels de méthode pourrait conduire un utilisateur de l'API à définir un aspect de la "visibilité" en oubliant d'en définir d'autres auxquels je les oblige à réfléchir en exposant uniquement la seule méthode pour définir tous les aspects de la "visibilité" . De plus, lorsque l'utilisateur veut changer un aspect, il voudra presque toujours changer un autre aspect et peut maintenant le faire en un seul appel.
update
méthode:foo.update(pinned=true, globally=true)
. Ou:foo.update_pinned(true, globally=true)
. Ainsi, la réponse à votre question doit également prendre en compte les fonctionnalités du langage, car une bonne API pour la langue X peut ne pas être bonne pour la langue Y et vice versa.setSize(10, 20)
n'est pas aussi lisible quesetSize(width=10, height=20)
ourandom(distribution='gaussian', mean=0.5, deviation=1)
. Dans les langues avec des paramètres nommés imposés, les booléens peuvent transmettre exactement la même quantité d'informations que l'utilisation d'énumérations / constantes nommées, de sorte qu'ils peuvent être bons dans les API.Réponses:
Votre désir de ne pas le diviser en trois appels de méthode est parfaitement compréhensible, mais vous avez d'autres options en plus des paramètres booléens.
Vous pouvez utiliser des énumérations:
Ou même une énumération de drapeaux (si votre langue le prend en charge):
Ou vous pouvez utiliser un objet paramètre :
Le modèle d'objet de paramètre vous offre quelques autres avantages qui peuvent vous être utiles. Il facilite le contournement et la sérialisation d'un ensemble de paramètres, et vous pouvez facilement donner aux paramètres non spécifiés des valeurs "par défaut".
Ou vous pouvez simplement utiliser des paramètres booléens. Microsoft semble le faire tout le temps avec l'API .NET Framework, vous pouvez donc simplement hausser les épaules et dire "si c'est assez bon pour eux, c'est assez bon pour moi".
la source
Évidemment, il y a toujours des exceptions à la règle, mais comme vous l'avez bien expliqué par vous-même, il y a certains avantages à avoir une API lisible. Les arguments booléens sont particulièrement gênants, car 1) ils ne révèlent pas d'intention et 2) ils impliquent que vous appeliez une fonction, alors que vous devriez en avoir deux, car des choses différentes vont se produire en fonction du drapeau booléen.
La question principale est plutôt: combien d'efforts voulez-vous investir pour rendre votre API plus lisible? Plus il est tourné vers l'extérieur, plus l'effort peut facilement être justifié. S'il s'agit d'une API qui n'est utilisée que par une autre unité, ce n'est pas si grave. Si vous parlez d'une API REST dans laquelle vous prévoyez de laisser le monde entier perdre dessus, vous pouvez également investir davantage d'efforts pour la rendre plus compréhensible.
Quant à votre exemple, il existe une solution simple: Apparemment, dans votre cas, la visibilité n'est pas seulement une chose vraie ou fausse. Au lieu de cela, vous avez tout un ensemble de choses que vous considérez comme de la "visibilité". Une solution pourrait être d'introduire quelque chose comme une
Visibility
classe, qui couvre tous ces différents types de visibilité. Si vous appliquez davantage le modèle Builder pour les créer, vous pouvez vous retrouver avec du code comme celui-ci:la source