Supposons que j'ai un objet personnalisé, étudiant :
public class Student{
public int _id;
public String name;
public int age;
public float score;
}
Et une classe, Window , utilisée pour afficher les informations d'un étudiant :
public class Window{
public void showInfo(Student student);
}
Cela semble assez normal, mais j’ai trouvé que Window n’était pas assez facile à tester individuellement, car il avait besoin d’un véritable objet Student pour appeler la fonction. J'essaie donc de modifier showInfo pour qu'il n'accepte pas d' objet Student directement:
public void showInfo(int _id, String name, int age, float score);
pour qu'il soit plus facile de tester Windows individuellement:
showInfo(123, "abc", 45, 6.7);
Mais j'ai trouvé que la version modifiée posait un autre problème:
Modifier un étudiant (par exemple: ajouter de nouvelles propriétés) nécessite de modifier la signature de méthode de showInfo
Si Student avait plusieurs propriétés, la méthode-signature de Student serait très longue.
Donc, en utilisant des objets personnalisés en tant que paramètre ou en acceptant chaque propriété d'objets en paramètre, laquelle est la plus facile à gérer?
showInfo
nécessite une vraie chaîne, un vrai float et deux vrais ints. En quoi la fourniture d'unString
objet réel est-elle meilleure que celle d'unStudent
objet réel ?int
paramètres. Sur le site de l'appel, il n'est pas vérifié que vous les transmettez dans le bon ordre. Et si vous échangezid
etage
, oufirstName
etlastName
? Vous introduisez un point d'échec potentiel qui peut être très difficile à détecter jusqu'à ce qu'il explose, et vous l'ajoutez à chaque site d'appel .showForm(bool, bool, bool, bool, int)
méthode - j'adore ceux-ci ...Réponses:
L'utilisation d'un objet personnalisé pour regrouper les paramètres associés est en fait un modèle recommandé. En tant que refactoring, il s’appelle Introduce Parameter Object .
Votre problème est ailleurs. Tout d'abord, générique
Window
ne doit rien savoir sur Student. Au lieu de cela, vous devriez avoir une sorte deStudentWindow
qui sait seulement afficherStudents
. Deuxièmement, la création d'uneStudent
instance à tester ne pose aucun problèmeStudentWindow
tantStudent
qu'elle ne contient aucune logique complexe qui compliquerait considérablement les testsStudentWindow
. Si cette logique existe,Student
il est préférable de créer une interface et de s'en moquer.la source
Student
regroupement est logique et est susceptible de se retrouver dans d'autres domaines de l'application.Student
, ce serait un objet entier à conservera.b.c
si votre méthode prenda
. Si votre méthode en arrive au point où il vous faut environ plus de 4 paramètres ou 2 niveaux d’accession à la propriété, il faudra probablement en tenir compte. Notez également qu'il s'agit d'une directive - comme toutes les autres directives, elle requiert la discrétion de l'utilisateur. Ne le suivez pas aveuglément.Vous dites que c'est
Mais vous pouvez simplement créer un objet étudiant à transmettre à votre fenêtre:
Cela ne semble pas beaucoup plus complexe d'appeler.
la source
Student
réfère à aUniversity
, qui fait référence à de nombreuxFaculty
s etCampus
s, avecProfessor
s etBuilding
s, dont aucunshowInfo
n’utilise réellement, mais vous n’avez défini aucune interface permettant aux tests de "savoir" cela et de ne fournir que l’élève correspondant. données, sans construire l'ensemble de l'organisation. L'exempleStudent
est un objet de données simple et, comme vous le dites, les tests devraient être heureux de fonctionner avec cet objet.En termes simples:
Modifier:
Comme @ Tom.Bowen89 l'a déclaré, tester la méthode showInfo n'est pas beaucoup plus complexe:
la source
la source
Steve McConnell dans Code Complete a traité de cette question en abordant les avantages et les inconvénients de la transmission d'objets à des méthodes au lieu d'utiliser des propriétés.
Pardonnez-moi si je me trompe dans certains détails, je travaille de mémoire car cela fait plus d'un an que j'ai accès au livre:
Il en arrive à la conclusion qu'il vaut mieux ne pas utiliser d'objet, mais envoyer uniquement les propriétés absolument nécessaires à la méthode. La méthode ne devrait pas avoir à connaître quoi que ce soit à propos de l'objet en dehors des propriétés qu'il utilisera dans le cadre de ses opérations. De plus, avec le temps, si l'objet est modifié, cela pourrait avoir des conséquences inattendues sur la méthode utilisant l'objet.
Il a également expliqué que si vous vous retrouviez avec une méthode qui acceptait beaucoup d'arguments différents, c'est probablement un signe que la méthode en fait trop et qu'elle devrait être décomposée en plusieurs méthodes plus petites.
Cependant, parfois, parfois, vous avez réellement besoin de beaucoup de paramètres. L'exemple qu'il donne serait celui d'une méthode qui construit une adresse complète, en utilisant de nombreuses propriétés d'adresse différentes (bien que cela puisse être obtenu en utilisant un tableau de chaînes lorsque vous y réfléchissez).
la source
Student
dans ce cas). Et c’est de cette façon que les tests informent le design , englobant complètement la réponse la plus votée tout en maintenant l’intégrité du design.Il est beaucoup plus facile d'écrire et de lire des tests si vous passez l'objet entier:
En comparaison,
La ligne pourrait être écrite, si vous passez les valeurs séparément, comme:
où l'appel de méthode réelle est enterré quelque part comme
Pour être précis, le fait que vous ne puissiez pas placer l'appel de méthode dans le test est un signe que votre API est mauvaise.
la source
Vous devriez transmettre ce qui a du sens, quelques idées:
Plus facile à tester. Si le ou les objets doivent être édités, qu'est-ce qui nécessite le moins de refactoring? La réutilisation de cette fonction à d’autres fins est-elle utile? Quelle est la quantité minimale d'informations dont j'ai besoin pour fournir cette fonction? (En le séparant (cela peut vous permettre de réutiliser ce code), méfiez-vous de la possibilité de créer cette fonction, puis de tout goulot d'étranglement pour utiliser exclusivement cet objet.)
Toutes ces règles de programmation ne sont que des guides pour vous aider à réfléchir dans la bonne direction. Ne construisez tout simplement pas une bête de code - si vous n'êtes pas sûr et que vous avez juste besoin de continuer, choisissez une direction / la vôtre ou une suggestion ici, et si vous arrivez à un point où vous pensez "oh, j'aurais dû le faire ainsi chemin '- vous pouvez probablement ensuite revenir en arrière et le refacturer assez facilement. (Par exemple, si vous avez une classe d'enseignant - elle nécessite uniquement la même propriété que Étudiant et vous modifiez votre fonction pour accepter n'importe quel objet du formulaire Personne.)
Je serais plus enclin à laisser l'objet principal transmis, car la façon dont je le code va expliquer plus facilement le fonctionnement de cette fonction.
la source
Une méthode courante consiste à insérer une interface entre les deux processus.
Cela devient parfois un peu compliqué, mais les choses deviennent un peu plus ordonnées en Java si vous utilisez une classe interne.
Vous pouvez ensuite tester la
Window
classe en lui donnant simplement un fauxHasInfo
objet.Je soupçonne que ceci est un exemple du modèle de décorateur .
Ajoutée
Il semble y avoir une certaine confusion causée par la simplicité du code. Voici un autre exemple qui peut mieux démontrer la technique.
la source
Student
etString
ici pour le type de retour est purement à des fins de démonstration. Il y aurait probablement des paramètres supplémentairesgetInfo
tels quePane
dessiner. Le concept ici est de transmettre des composants fonctionnels en tant que décorateurs de l'objet d'origine .HasInfo
objets.Student
sait être un.getInfo
retour passe vide unPane
à tirer à, puis la mise en œuvre (dans laStudent
classe) est soudainement couplé à balancer ou tout ce que vous utilisez. Si vous lui faites renvoyer une chaîne et que vous prenez 0 paramètres, votre interface utilisateur ne saura pas quoi faire avec la chaîne sans hypothèses magiques et couplage implicite. Si vous faitesgetInfo
réellement renvoyer un modèle de vue avec des propriétés pertinentes, votreStudent
classe est à nouveau couplée à la logique de présentation. Je pense qu'aucune de ces alternatives n'est souhaitableVous avez déjà beaucoup de bonnes réponses, mais voici quelques suggestions supplémentaires qui pourraient vous permettre de voir une solution alternative:
Votre exemple montre qu'un étudiant (clairement un objet du modèle) est passé à une fenêtre (apparemment un objet au niveau de la vue). Un objet Contrôleur ou Présentateur intermédiaire peut être utile si vous n'en avez pas déjà, ce qui vous permet d'isoler votre interface utilisateur de votre modèle. Le contrôleur / présentateur doit fournir une interface pouvant être utilisée pour le remplacer pour les tests d'interface utilisateur, et doit utiliser des interfaces pour faire référence aux objets de modèle et aux objets de vue afin de pouvoir l'isoler des deux à des fins de test. Vous devrez peut-être fournir un moyen abstrait de créer ou de charger ceux-ci (par exemple, des objets Factory, des objets Repository, ou similaire).
Le transfert de parties pertinentes de vos objets de modèle dans un objet de transfert de données est une approche utile pour l’interfaçage lorsque votre modèle devient trop complexe.
Il se peut que votre élève enfreigne le principe de séparation des interfaces. Si tel est le cas, il pourrait être avantageux de le scinder en plusieurs interfaces plus faciles à utiliser.
Le chargement différé peut faciliter la construction de graphiques d'objets volumineux.
la source
C'est en fait une question décente. Le vrai problème ici est l'utilisation du terme générique "objet", qui peut être un peu ambigu.
Généralement, dans un langage POO classique, le terme "objet" en est venu à signifier "instance de classe". Les instances de classe peuvent être assez lourdes - propriétés publiques et privées (et entre celles-ci), méthodes, héritage, dépendances, etc. Vous ne voudriez pas vraiment utiliser quelque chose comme ça pour simplement transmettre certaines propriétés.
Dans ce cas, vous utilisez un objet en tant que conteneur contenant simplement certaines primitives. En C ++, les objets tels que ceux-ci étaient appelés
structs
(et existent toujours dans des langages tels que C #). En fait, les structures ont été conçues exactement pour l'usage dont vous parlez - elles ont regroupé des objets et des primitives apparentés lorsqu'ils avaient une relation logique.Cependant, dans les langages modernes, il n'y a vraiment aucune différence entre une structure et une classe lorsque vous écrivez le code , vous pouvez donc utiliser un objet. (Dans les coulisses, cependant, il y a certaines différences dont vous devriez être conscient - par exemple, une structure est un type de valeur, pas un type de référence.) Fondamentalement, tant que vous gardez votre objet simple, il sera facile tester manuellement. Les langages et les outils modernes vous permettent toutefois d’atténuer un peu ces problèmes (via des interfaces, des frameworks moqueurs, l’injection de dépendances, etc.)
la source
Student
) en modèles de vue (StudentInfo
ouStudentInfoViewModel
autres), mais cela pourrait ne pas être nécessaire.