12
votes

Question design: Passez les champs que vous utilisez ou passez l'objet?

Je vois souvent deux stratégies conflictuelles pour les interfaces de méthode, résumées de manière vague comme suit:

// Form 1: Pass in an object.
double calculateTaxesOwed(TaxForm f) { ... }

// Form 2: Pass in the fields you'll use.
double calculateTaxesOwed(double taxRate, double income) { ... }

// use of form 1:
TaxForm f = ...
double payment = calculateTaxesOwed(f);

// use of form 2:
TaxForm f = ...
double payment = calculateTaxesOwed(f.getTaxRate(), f.getIncome());


0 commentaires

12 Réponses :


3
votes

Cela dépend de l'intention de votre méthode.

Si la méthode est conçue pour fonctionner spécifiquement avec cet objet et uniquement cet objet, passez l'objet. Cela fait une belle encapsulation.

Mais, si la méthode est plus générale , vous voudrez probablement transmettre les paramètres individuellement . De cette façon, la méthode est plus susceptible d'être réutilisée lorsque les informations arrivent d'une autre source (c'est-à-dire différents types d'objets ou d'autres données dérivées).


0 commentaires

1
votes

Je ne pense pas que ça compte vraiment. Vous vous ouvrez aux effets secondaires si vous passez dans l'objet car il pourrait être muté. Cela pourrait cependant être ce que vous voulez. Pour atténuer cela (et pour faciliter les tests), vous êtes probablement mieux en train de passer l'interface plutôt que du type concret. La prestation est que vous n'avez pas besoin de modifier la signature de la méthode si vous souhaitez accéder à un autre champ de l'objet.

Passer tous les paramètres permet plus de préciser ce que les besoins de type et pourraient faciliter la test (bien que si vous utilisez l'interface, cela est moins avantages). Mais vous aurez plus de refactoring.

juge chaque situation sur ses mérites et choisissez le moins douloureux.


0 commentaires

0
votes

Un avantage du premier formulaire est

  1. Abstraction - Programmation à une interface plutôt que de mise en œuvre. Il facilitera la maintenance de votre code à long terme parce que vous pouvez modifier la mise en œuvre de la taxe sans affecter le code client aussi longtemps que l'interface de la taxe ne change pas.

0 commentaires

2
votes

Je recommande vivement la deuxième solution - CalculateTaTaxesEfed () calcule certaines données, il faut donc une entrée numérique. La méthode n'a absolument rien à voir avec l'interface utilisateur et ne doit à son tour pas consommer un formulaire comme entrée, car vous souhaitez que votre logique commerciale soit séparée de votre interface utilisateur.

La méthode exécutant le calcul doit (habituellement) n'appartient même pas au même modul que l'interface utilisateur. Dans ce cas, vous obtenez une dépendance circulaire car l'interface utilisateur nécessite la logique commerciale et la logique d'entreprise nécessite le formulaire d'interface utilisateur - une très forte indication que quelque chose ne va pas (mais pourrait encore être résolu à l'aide de la programmation basée sur l'interface).

mise à jour

Si le formulaire de taxe n'est pas un formulaire d'interface utilisateur, les choses changent un peu. Dans ce cas, je suggère d'exposer la valeur à l'aide d'une méthode d'instance greatowedtaxes () ou d'une propriété d'instance owedtaxes du de la forme de taxe mais je n'utiliserais pas une méthode statique. Si le calcul peut être réutilisé ailleurs, on pourrait toujours créer une méthode d'assistance statique consommant les valeurs, non le formulaire et appeler cette méthode d'assistance à partir de la méthode d'instance ou de la propriété.


3 commentaires

Je lis "formulaire de taxe" ici comme "formulaire 1040", pas "un écran d'interface utilisateur utilisé pour la saisie de données".


(Oh, et pour les lecteurs non américains, le 1040 est la principale forme utilisée pour soumettre des déclarations de revenus personnelles aux États-Unis. Donc, je suppose que le "Taxform" est une classe qui encapsule les informations et les procédures liées à un document de taxe spécifique. )


Désolé pour la confusion. Oui, par "formulaire" Je voulais dire "un ensemble de données et de comportements représentant une soumission de l'impôt sur le revenu auto-calculant".



6
votes

En toute honnêteté, cela dépend de la méthode en question.

Si la méthode est logique sans l'objet, le deuxième formulaire est plus facile à réutiliser et supprime un couplage entre les deux classes.

Si la méthode s'appuie sur l'objet, alors assez juste, passez l'objet.

Il y a probablement un bon argument pour une troisième forme où vous passez une interface conçue pour fonctionner avec cette méthode. Vous donne la clarté de la première forme avec la flexibilité de la seconde.


0 commentaires

1
votes

Passage Just Les arguments peuvent être plus faciles à tester l'unité , car vous n'avez pas besoin de se moquer de des objets entiers pleins de données uniquement pour tester des fonctionnalités essentiellement statiques. S'il n'y a que deux champs utilisés, de nombreux champs de l'objet, je me pencherais vers le passage de ces champs, tout le reste étant égal.

qui dit, lorsque vous vous retrouvez avec six, sept champs ou plus, il est temps d'envisager de passer à l'objet entier ou d'un sous-ensemble des champs dans une classe de charge utile (ou structure / structure / Dictionnaire, selon le style de la langue). Les signatures de méthodes longues sont généralement déroutantes.

L'autre option consiste à en faire une méthode de classe, de sorte que vous n'avez rien à passer. Il est moins pratique de tester, mais la peine d'être envisagée lorsque votre méthode n'est jamais utilisée que sur les données d'un objet de taxes.


0 commentaires

0
votes

C'est la même chose que "introduire l'objet de paramètre" du livre de Martin Fowler sur le refactoring. Fowler suggère de remplir ce refactoring s'il existe un groupe de paramètres qui ont tendance à être adoptés.


0 commentaires

0
votes

Si vous croyez en la loi de Demeter, vous favoriseriez en passant exactement ce qui est nécessaire:

http://en.wikipedia.org/wiki/law_of_demeter

http://www.c2.com/cgi/wiki?LawofDemeter


0 commentaires

0
votes

séparation de l'interface utilisateur et des données à manipuler

Dans votre cas, il vous manque une classe intermédiaire, disons, taxInfo, représentant l'entité à taxer. La raison en est que l'interface utilisateur (la forme) et la logique commerciale (comment calculer le taux d'imposition) figurent sur deux "pistes de changement" différentes, une modification avec la technologie de présentation ("the web", "the web 2.0", "wpf" ,. ..), l'autre change avec leghantse. Définissez une interface claire entre eux.


Discussion générale, en utilisant un exemple:

Considérez une fonction pour créer un bitmap pour une carte de visite. Est le but de la fonction

(1) // formats un titre de carte de visite à partir du prénom et du nom de famille

ou

(2) // formats un titre de carte businnes à partir d'un personne record

La première option est plus générique, avec un couplage plus faible, qui est généralement préférable. Cependant, dans de nombreux cas moins robustes contre les demandes de changement - par ex. Considérons "Cas 2017: Ajoutez des personnes initiales à la carte de visite".

Modification de la mise en œuvre (Ajout de la personne.Initial) est généralement plus facile et plus rapide que la modification de l'interface.

Le choix est en définitive quel type de changements vous attendez: est-il plus probable que davantage d'informations à partir d'une personne est requise ou est-il plus probable que vous souhaitiez créer des titres de carte de visite pour d'autres structures de données que personne ?

Si cela est "indécis", ANFD que vous ne pouvez pas utiliser l'OPF à des fins (1) ou (2), je préfère aller avec (2), pour la propreté syntaxique.


0 commentaires

1
votes

Je réalise que cela est largement un artefact de l'exemple utilisé et il peut donc ne pas s'appliquer dans de nombreux cas mondiaux réels, mais si la fonction est si fortement attachée à une classe spécifique, alors ne devrait-il pas être: < PRE> XXX

Cela me semble plus approprié pour moi qu'un document fiscal assumerait la responsabilité elle-même du calcul des taxes pertinentes plutôt que de faire tomber cette responsabilité sur une fonction d'utilité, en particulier étant donné que différents formulaires d'impôt ont tendance à utiliser différentes tables d'impôt ou méthodes de calcul.


0 commentaires

0
votes

Si j'ai été fait pour choisir l'un des deux, j'irais toujours avec le second - que si vous constatez que vous (pour une raison quelconque) besoin de caculer les taxes dus, mais vous n'avez pas d'instance de < Code> Taxform ?

Il s'agit d'un exemple assez trivial, mais j'ai vu des cas où une méthode faisant une tâche relativement simple avait des intrants complexes difficiles à créer, ce qui rend la méthode beaucoup plus difficile à utiliser que Cela aurait dû être. (L'auteur n'a tout simplement pas considéré que d'autres personnes voudront peut-être utiliser cette méthode!)

personnellement, pour rendre le code plus lisible, je voudrais probablement avoir les deux: xxx < / Pré>

Ma règle echétique est que dans la mesure du possible, avoir une méthode qui prend exactement l'entrée dont il a besoin - ses méthodes d'emballage très faciles à écrire.


0 commentaires

0
votes

Personnellement, je vais aller avec # 2 car il est beaucoup plus clair de ce que la méthode a besoin. En passant la taxe (si c'est ce que c'est ce que je pense que c'est, comme un formulaire Windows) est une sorte de smelly et me fait de me faire grincer un peu (> _ <).

J'utiliserais la première variation que si vous passez une DTO spécifique au calcul, comme l'objet IncometaxCalculSInfo qui contiendra le taxrate et le revenu et quoi que ce soit nécessaire pour calculer le résultat final dans la méthode, mais jamais quelque chose comme un Formulaire Windows / Web.


0 commentaires