Disons que j'ai une fonction IsAdmin
qui vérifie si un utilisateur est un administrateur. Disons également que la vérification de l'administrateur est effectuée en faisant correspondre l'identifiant, le nom et le mot de passe de l'utilisateur à une sorte de règle (sans importance).
Dans ma tête, il y a alors deux signatures de fonction possibles pour cela:
public bool IsAdmin(User user);
public bool IsAdmin(int id, string name, string password);
Je choisis le plus souvent le deuxième type de signature, en pensant que:
- La signature de fonction donne au lecteur beaucoup plus d'informations
- La logique contenue dans la fonction n'a pas besoin de connaître la
User
classe - Il en résulte généralement un peu moins de code dans la fonction
Cependant, je remets parfois en cause cette approche et réalise également qu’à un moment donné, elle deviendrait difficile à manier. Si, par exemple, une fonction mappait entre dix champs d'objet différents dans un bool résultant, j'enverrais évidemment l'objet entier. Mais à part un exemple aussi frappant que celui-là, je ne vois aucune raison de transmettre l'objet réel.
J'apprécierais tous les arguments pour l'un ou l'autre style, ainsi que vos observations générales.
Je programme dans les styles à la fois orienté objet et fonctionnel, la question doit donc être considérée comme concernant tous les idiomes.
la source
Réponses:
Personnellement, je préfère la première méthode de juste
IsAdmin(User user)
Il est beaucoup plus facile à utiliser et si vos critères pour IsAdmin changent ultérieurement (peut-être en fonction de rôles ou d'isActive), vous n'avez pas besoin de réécrire la signature de votre méthode partout.
Il est également probablement plus sûr de ne pas annoncer les propriétés qui déterminent si un utilisateur est un administrateur ou non, ou de transmettre la propriété de mot de passe partout. Et syrion insiste sur le fait que que se passe-t-il lorsque votre
id
ne correspond pas à laname
/password
?La longueur du code dans une fonction ne devrait pas vraiment avoir d'importance, à condition que la méthode fasse son travail, et je préférerais de loin un code d'application plus court et plus simple que le code de la méthode auxiliaire.
la source
La première signature est supérieure car elle permet l'encapsulation de la logique liée à l'utilisateur dans l'
User
objet même. Il n’est pas avantageux d’avoir une logique pour la construction du tuple "id, name, password" sur votre base de code. De plus, cela complique laisAdmin
fonction: que se passe-t-il si quelqu'un passe dans unid
fichier qui ne correspond pas auname
, par exemple? Que faire si vous voulez vérifier si un utilisateur donné est un administrateur d’un contexte dans lequel vous ne devriez pas connaître son mot de passe?En outre, comme une note sur votre troisième point en faveur du style avec des arguments supplémentaires; il peut en résulter "moins de code dans la fonction", mais où va cette logique? Ça ne peut pas disparaître! Au lieu de cela, il est réparti autour de chaque endroit où la fonction est appelée. Pour enregistrer cinq lignes de code au même endroit, vous avez payé cinq lignes par utilisation de la fonction!
la source
user.IsAdmin()
mieux?IsAdmin
une forme ou une fonction spécifique, cela ne devrait pas être lié à l'utilisateurLe premier, mais pas (seulement) pour les raisons que les autres ont données.
Ceci est le type sûr. D'autant plus que l'utilisateur est un type que vous avez défini vous-même, il y a peu ou pas d'occasions de transposer ou d'activer des arguments. Il fonctionne clairement sur les utilisateurs et n'est pas une fonction générique acceptant les entiers et les deux chaînes. C’est une grande partie de l’intérêt d’utiliser un langage sûr comme Java ou C # auquel votre code ressemble. Si vous posez des questions sur une langue spécifique, vous pouvez ajouter une étiquette pour cette langue à votre question.
Ici, tout int et deux chaînes feront l'affaire. Qu'est-ce qui vous empêche de transposer le nom et le mot de passe? La seconde demande plus de travail et permet plus d’erreurs.
Vraisemblablement, les deux fonctions requièrent que user.getId (), user.getName () et user.getPassword () soient appelées, soit à l'intérieur de la fonction (dans le premier cas), soit avant l'appel de la fonction (dans le second); la quantité de couplage est la même de toute façon. En fait, cette fonction n'étant valide que sur les utilisateurs, en Java, j'éliminerais tous les arguments et en ferais une méthode d'instance sur les objets utilisateur:
Étant donné que cette fonction est déjà étroitement liée aux utilisateurs, il est logique d’en faire une partie de ce qu’un utilisateur est.
PS Je suis sûr que ce n'est qu'un exemple, mais il semble que vous stockiez un mot de passe. Vous devriez plutôt stocker uniquement un hachage de mot de passe cryptographiquement sécurisé. Lors de la connexion, le mot de passe fourni doit être haché de la même manière et comparé au hachage stocké. L'envoi de mots de passe en clair doit être évité. Si vous ne respectez pas cette règle et que isAdmin (int Str Str) devait enregistrer le nom d'utilisateur, puis si vous transposiez le nom et le mot de passe dans votre code, le mot de passe pourrait être enregistré à la place, ce qui crée un problème de sécurité (n'écrivez pas de mots de passe dans les journaux ) et constitue un autre argument en faveur du passage d’un objet à la place de ses composants (ou de l’utilisation d’une méthode de classe).
la source
Si votre langue de choix ne passe pas les structures par valeur, alors
(User user)
variante semble meilleure. Et si un jour vous décidiez de supprimer le nom d'utilisateur et d'utiliser simplement l'ID / mot de passe pour identifier l'utilisateur?En outre, cette approche conduit inévitablement à des appels de méthode longs et excessifs, ou à des incohérences. Passer 3 arguments est-il correct? Que diriez-vous de 5? Ou 6? Pourquoi penser à cela, si passer un objet est peu coûteux en termes de ressources (même moins cher que de passer 3 arguments, probablement)?
Et je suis (en quelque sorte) en désaccord avec le fait que la deuxième approche donne au lecteur plus d'informations - intuitivement, l'appel à la méthode demande "si l'utilisateur possède des droits d'administrateur", et non "si la combinaison donnée id / nom / mot de passe possède des droits d'administrateur".
Ainsi, à moins que le passage de l'
User
objet ne conduise à la copie d'une grande structure, la première approche est plus propre et plus logique.la source
J'aurais probablement les deux. Le premier en tant qu'API externe, avec un espoir de stabilité dans le temps. Le second en tant qu'implémentation privée appelée par l'API externe.
Si, par la suite, je devais modifier la règle de vérification, il me suffirait d'écrire une nouvelle fonction privée avec une nouvelle signature appelée par l'externe.
Cela a l'avantage d'être facile à changer d'implémentation interne. En outre, il est très courant qu'à un moment donné, les deux fonctions soient disponibles simultanément et que vous appeliez l'une ou l'autre en fonction d'un changement de contexte externe (par exemple, d'anciens utilisateurs coexistants et de nouveaux utilisateurs possédant des champs différents peuvent coexister).
En ce qui concerne le titre de la question, dans un cas comme dans l'autre, vous fournissez le minimum d'informations nécessaires. Un utilisateur semble être la plus petite structure de données liée à l'application contenant les informations. Les trois champs id, password et name semblent être les plus petites données d'implémentation réellement nécessaires, mais ce ne sont pas vraiment des objets de niveau Application.
En d'autres termes, si vous utilisiez une base de données, un utilisateur serait un enregistrement, tandis que l'id, le mot de passe et le login seraient les champs de cet enregistrement.
la source
Il existe un point négatif pour l'envoi de classes (types de référence) aux méthodes, à savoir la vulnérabilité d'affectation en masse . Imaginez qu'au lieu de
IsAdmin(User user)
méthode, j'aiUpdateUser(User user)
méthode.Si
User
classe a une propriété booléenne appeléeIsAdmin
et que je ne la vérifie pas lors de l'exécution de ma méthode, je suis vulnérable à l'affectation en masse. GitHub a été attaqué par cette signature même en 2012.la source
J'irais avec cette approche:
L'utilisation du type d'interface en tant que paramètre pour cette fonction vous permet de passer des objets utilisateur, de définir des objets utilisateur fictifs à tester, et vous offre davantage de flexibilité pour l'utilisation et la maintenance de cette fonction.
J'ai également ajouté le mot clé
this
pour que la fonction devienne une méthode d'extension de toutes les classes IUser; De cette façon, vous pouvez écrire du code de manière plus conviviale et utiliser cette fonction dans les requêtes Linq. Plus simple encore serait de définir cette fonction dans IUser et User, mais je suppose qu’il ya une raison pour laquelle vous avez décidé de placer cette méthode en dehors de cette classe?J'utilise l'interface personnalisée à la
IID
placeint
car cela vous permet de redéfinir les propriétés de votre identifiant; par exemple , si vous avez besoin jamais de changer deint
àlong
,Guid
ou autre chose. C'est probablement exagéré, mais il est toujours bon d'essayer de rendre les choses flexibles de manière à ne pas être obligé de prendre des décisions précoces.NB: Votre objet IUser peut se trouver dans un espace de nom et / ou un assemblage différent de la classe User. Vous avez donc la possibilité de garder votre code utilisateur complètement séparé de votre code IsAdmin, avec le partage d'une seule bibliothèque référencée. Exactement ce que vous faites il y a un appel pour savoir comment vous pensez que ces objets seront utilisés.
la source
Avertissements:
Pour ma réponse, je vais me concentrer sur le problème du style et oublier si un identifiant, un identifiant et un mot de passe en clair constituent un bon jeu de paramètres à utiliser du point de vue de la sécurité. Vous devriez pouvoir extrapoler ma réponse à n'importe quel ensemble de données de base que vous utilisez pour connaître les privilèges de l'utilisateur ...
Je considère également
user.isAdmin()
être équivalent àIsAdmin(User user)
. C'est un choix à faire pour vous.Répondre:
Ma recommandation est à l'un ou l'autre utilisateur seulement:
Ou utilisez les deux, comme suit:
Raisons de la méthode publique:
Lorsque vous écrivez des méthodes publiques, c'est généralement une bonne idée de penser à la façon dont elles seront utilisées.
Dans ce cas particulier, vous appelez généralement cette méthode pour déterminer si un utilisateur donné est un administrateur. C'est une méthode dont vous avez besoin. Vous ne voulez vraiment pas que chaque appelant doive choisir les bons paramètres à envoyer (et risquer des erreurs en raison de la duplication de code).
Raisons de la méthode privée:
La méthode privée ne recevant que le minimum de paramètres requis peut parfois être une bonne chose. Parfois pas tellement.
L'un des avantages de la scission de ces méthodes est que vous pouvez appeler la version privée à partir de plusieurs méthodes publiques de la même classe. Par exemple, si vous vouliez (pour une raison quelconque) avoir une logique légèrement différente pour
Localuser
etRemoteUser
(peut-être un paramètre pour activer / désactiver les administrateurs distants?):En outre, si pour une raison quelconque vous devez rendre publique la méthode privée, vous le pouvez. C'est aussi simple que de changer
private
pourpublic
. Cela peut ne pas sembler être un gros avantage pour cette affaire, mais parfois, c'est vraiment le cas.De plus, lors de tests unitaires, vous serez capable de faire beaucoup plus de tests atomiques. Il est généralement très agréable de ne pas avoir à créer un objet utilisateur complet pour exécuter des tests sur cette méthode. Et si vous souhaitez une couverture complète, vous pouvez tester les deux appels.
la source
est mauvais pour les raisons énumérées. Cela ne veut pas dire
est automatiquement bon. En particulier , si l' utilisateur est un objet ayant des méthodes telles que :
getOrders()
,getFriends()
,getAdresses()
, ...Vous pouvez envisager de refactoriser le domaine avec un type UserCredentials contenant uniquement l'ID, le nom, le mot de passe et de le transmettre à IsAdmin au lieu de toutes les données utilisateur inutiles.
la source