Est-il suffisant de distinguer les méthodes uniquement par leur nom d'argument (et non par type) ou est-il préférable de les nommer de manière plus explicite?
Par exemple T Find<T>(int id)
vs T FindById<T>(int id)
.
Y a-t-il une bonne raison de le nommer de manière plus explicite (c'est-à-dire en ajoutant ById
) vs garder juste le nom de l'argument?
L'une des raisons pour lesquelles je peux penser est que les signatures des méthodes sont les mêmes mais qu'elles ont une signification différente.
FindByFirstName(string name)
et FindByLastName(string name)
Bien sûr, il y a une bonne raison de le nommer plus explicitement.
Ce n'est pas principalement la méthode définition qui devrait être explicite, mais la méthode tilisation. Et tandis que findById(string id)
et find(string id)
sont tous deux explicites, il y a une énorme différence entre findById("BOB")
et find("BOB")
. Dans le premier cas, vous savez que le littéral aléatoire est, en fait, un Id. Dans ce dernier cas, vous n'êtes pas sûr - il pourrait s'agir en fait d'un prénom ou de quelque chose d'autre.
Avantages de FindById () .
A l'épreuve du temps : Si vous commencez par Find(int)
, et que vous devez ensuite ajouter d'autres méthodes (FindByName(string)
, FindByLegacyId(int)
, FindByCustomerId(int)
, FindByOrderId(int)
, etc.), les gens comme moi ont tendance à passer beaucoup de temps à chercher FindById(int)
. Pas vraiment un problème si vous pouvez et allez changer Find(int)
en FindById(int)
une fois que cela sera nécessaire - l'épreuvage futur concerne ces si s.
Plus facile à lire . Find
est parfaitement bien si l'appel ressemble à record = Find(customerId);
Pourtant FindById
est légèrement plus facile à lire si c'est record = FindById(AFunction());
.
Cohérence . Vous pouvez appliquer systématiquement le modèle FindByX(int)
/FindByY(int)
partout, mais Find(int X)
/Find(int Y)
n'est pas possible car ils sont en conflit.
Avantages de Find ()
Find
est simple et direct, et avec operator[]
c'est l'un des 2 noms de fonctions les plus attendus dans ce contexte. (Certaines alternatives populaires étant get
, lookup
ou fetch
, selon le contexte).FindById(int id)
, nous pouvons facilement supprimer la redondance en la changeant en Find(int id)
, mais il y a un compromis - nous perdons une certaine clarté.Alternativement, vous pouvez obtenir les avantages des deux en utilisant des ID fortement typés:
CustomerRecord Find(Id<Customer> id)
// Or, depending on local coding standards
CustomerRecord Find(CustomerId id)
Implémentation de Id<>
: en tapant fortement les valeurs ID en C #
Les commentaires ici, ainsi que dans le lien ci-dessus, ont soulevé plusieurs préoccupations concernant Id<Customer>
Que je voudrais aborder:
CustomerId
et OrderID
sont de types différents (customerId1 = customerId2;
=> bon, customerId1 = orderId1;
=> mauvais), mais leur implémentation est presque identique, nous pouvons donc les implémenter soit avec copier coller, soit avec métaprogrammer. Bien qu'il soit utile de discuter de l'exposition ou de la dissimulation du générique, la métaprogrammation est à quoi servent les génériques.DoSomething(int customerId, int orderId, int productId)
. Les identifiants fortement saisis empêchent également d'autres problèmes, y compris celui auquel OP a posé des questions.int aVariable
. Il est facile de dire qu'un identifiant est conservé dans Id<Customer> aVariable
, Et nous pouvons même dire qu'il s'agit d'un identifiant client.String
est juste un wrapper autour de byte[]
. L'encapsulation ou l'encapsulation n'est pas en conflit avec une frappe forte.operator==
Et operator!=
, si vous ne voulez pas vous fier exclusivement à Equals
:.
public struct Id<T>: {
private readonly int _value ;
public Id(int value) { _value = value; }
public static explicit operator int(Id<T> id) { return id._value; }
}
Une autre façon de penser à ce sujet est d'utiliser le type de sécurité de la langue.
Vous pouvez implémenter une méthode telle que:
Find(FirstName name);
Où FirstName est un objet simple qui encapsule une chaîne qui contient le prénom et signifie qu'il ne peut y avoir aucune confusion quant à ce que fait la méthode, ni dans les arguments avec lesquels elle est appelée.
Je voterai pour une déclaration explicite comme FindByID .... Le logiciel devrait être construit pour le changement. Il doit être ouvert et fermé (SOLIDE). Ainsi, la classe est ouverte pour ajouter une méthode de recherche similaire, comme disons FindByName .. etc.
Mais FindByID est fermé et sa mise en œuvre est testée unitaire.
Je ne proposerai pas de méthodes avec des prédicats, celles-ci sont bonnes au niveau générique. Et si, sur la base du champ (ByID), vous disposez d'une méthodologie différente.
Je suis surpris que personne n'ait suggéré d'utiliser un prédicat comme celui-ci:
User Find(Predicate<User> predicate)
Avec cette approche, non seulement vous réduisez la surface de votre API, mais vous donnez également plus de contrôle à l'utilisateur qui l'utilise.
Si cela ne suffit pas, vous pouvez toujours l'étendre à vos besoins.