Est-il correct d’avoir une classe avec seulement des propriétés à des fins de refactoring?

J’ai une méthode qui prend 30 parameters. J’ai pris les parameters et les ai placés dans une classe, de sorte que je puisse simplement passer un paramètre (la classe) dans la méthode. Est-il parfaitement correct, dans le cas du refactoring, de transmettre un object qui encapsule tous les parameters, même si c’est tout ce qu’il contient?

C’est une bonne idée. C’est généralement la façon dont les contrats de données sont exécutés dans WCF, par exemple.

Un avantage de ce modèle est que si vous ajoutez un nouveau paramètre, le consommateur de la classe n’a pas besoin de changer pour append le paramètre.

Comme le mentionne David Heffernan, il peut aider à documenter le code:

FrobRequest frobRequest = new FrobRequest { FrobTarget = "Joe", Url = new Uri("http://example.com"), Count = 42, }; FrobResult frobResult = Frob(frobRequest); 

Tandis que d’autres réponses ici soulignent correctement que transmettre une instance d’une classe est préférable à la transmission de 30 parameters, sachez qu’un grand nombre de parameters peut être le symptôme d’un problème sous-jacent.

Par exemple, le nombre de parameters des méthodes statiques augmente souvent, car elles auraient dû être des méthodes d’instance depuis toujours, et vous transmettez beaucoup d’informations qui pourraient plus facilement être conservées dans une instance de cette classe.

Vous pouvez également chercher des moyens de regrouper les parameters en objects d’un niveau d’abstraction supérieur. Placer un tas de parameters sans rapport dans une seule classe est une solution de dernier recours.

Voir combien de parameters sont trop nombreux? pour plus d’idées à ce sujet.

C’est un bon début. Mais maintenant que vous avez cette nouvelle classe, envisagez de transformer votre code en deux. Déplacez la méthode qui prend cette classe comme paramètre dans votre nouvelle classe (en passant bien sûr une instance de la classe d’origine en tant que paramètre). Maintenant, vous avez une grande méthode, seule dans une classe, et il sera plus facile de la diviser en méthodes testables, plus petites et plus faciles à gérer. Certaines de ces méthodes peuvent revenir à la classe d’origine, mais une partie équitable restra probablement dans votre nouvelle classe. Vous avez dépassé Introduire un object de paramètre pour remplacer la méthode par un object Method .

Avoir une méthode avec trente parameters est un signe assez fort que la méthode est trop longue et trop compliquée. Trop difficile à déboguer, trop difficile à tester. Donc, vous devriez faire quelque chose, et Introduce Parameter Object est un bon endroit pour commencer.

Bien que le refactoring sur un object de paramètre ne soit pas en soi une mauvaise idée, il ne devrait pas être utilisé pour cacher le problème qu’une classe ayant besoin de 30 données fournies par ailleurs pourrait encore être une odeur de code. Le refactoring Introduce Parameter Object devrait probablement être considéré comme une étape du processus de refactoring plus large que la fin de cette procédure.

L’une des préoccupations qu’elle n’aborde pas vraiment est celle de Feature Envy. Est-ce que le fait que la classe transmise à l’object Parameter s’intéresse tellement aux données d’une autre classe n’indique pas que les méthodes qui fonctionnent sur ces données doivent peut-être être déplacées vers les données? Il est vraiment préférable d’identifier les grappes de méthodes et de données qui appartiennent ensemble et de les regrouper en classes, augmentant ainsi l’encapsulation et rendant votre code plus flexible.

Après plusieurs itérations du comportement de décomposition et des données sur lesquelles il opère dans des unités séparées, vous devriez constater que vous n’avez plus de classes avec un nombre énorme de dépendances, ce qui est toujours un meilleur résultat car cela rendra votre code plus souple.

C’est une excellente idée et une solution très commune au problème. Les méthodes avec plus de 2 ou 3 parameters deviennent de plus en plus difficiles à comprendre.

Encapsuler tout cela dans une seule classe rend le code beaucoup plus clair. Comme vos propriétés ont des noms, vous pouvez écrire du code auto-documenté comme ceci:

 params.height = 42; params.width = 666; obj.doSomething(params); 

Naturellement, lorsque vous avez beaucoup de parameters, l’alternative basée sur l’identification de position est tout simplement horrible.

Un autre avantage est que l’ajout de parameters supplémentaires au contrat d’interface peut être effectué sans forcer les modifications sur tous les sites d’appels. Cependant, ce n’est pas toujours aussi sortingvial qu’il y paraît. Si différents sites d’appels requièrent des valeurs différentes pour le nouveau paramètre, il est alors plus difficile de les rechercher qu’avec l’approche basée sur les parameters. Dans l’approche basée sur les parameters, l’ajout d’un nouveau paramètre force une modification à chaque site d’appel pour fournir le nouveau paramètre et vous pouvez laisser le compilateur faire le travail pour les trouver tous.

Martin Fowler appelle ceci Introduce Parameter Object dans son livre Refactoring . Avec cette citation, peu de gens diraient que c’est une mauvaise idée.

30 parameters est un gâchis. Je pense que c’est plus joli d’avoir une classe avec les propriétés. Vous pouvez même créer plusieurs “classes de parameters” pour des groupes de parameters appartenant à la même catégorie.

Vous pouvez également envisager d’utiliser une structure plutôt qu’une classe.

Mais ce que vous essayez de faire est très courant et une excellente idée!

Il peut être raisonnable d’utiliser une classe Plain Old Data , que vous soyez ou non en train de refactoriser. Je suis curieux de savoir pourquoi vous pensiez que ce ne serait pas le cas.

Peut-être que les parameters facultatifs et nommés de C # 4.0 sont une bonne alternative à cela?

Quoi qu’il en soit, la méthode que vous décrivez peut également être utile pour abstraire le comportement des programmes. Par exemple, vous pouvez avoir une fonction standard SaveImage(ImageSaveParameters saveParams) dans une interface où ImageSaveParameters est également une interface et peut avoir des parameters supplémentaires en fonction du format de l’image. Par exemple, JpegSaveParameters a une propriété Quality alors que PngSaveParameters a une propriété BitDepth .

C’est ce que fait la boîte de dialog de sauvegarde dans Paint.NET, ce qui en fait un exemple très réel.

Comme indiqué précédemment: c’est le bon pas à faire, mais considérez également les points suivants:

  • votre méthode pourrait être trop complexe (vous devriez envisager de le diviser en plusieurs méthodes, ou même de le transformer en une classe distincte)
  • si vous créez la classe pour les parameters, rendez-la immuable
  • Si beaucoup de parameters peuvent être nuls ou avoir une valeur par défaut, vous pouvez utiliser le modèle de générateur pour votre classe.

Tant de bonnes réponses ici. Je voudrais append mes deux cents.

L’object Parameter est un bon début. Mais il y a plus à faire. Considérez ce qui suit (exemples de rbuy):

/ 1 / Au lieu de regrouper simplement tous les parameters, voir s’il peut y avoir un regroupement significatif des parameters. Vous pourriez avoir besoin de plusieurs objects de paramètre.

 def display_line(startPoint, endPoint, option1, option2) 

pourrait devenir

 def display_line(line, display_options) 

/ 2 / L’object Parameter peut avoir un nombre de propriétés inférieur au nombre de parameters d’origine.

 def double_click?(cursor_location1, control1, cursor_location2, control2) 

pourrait devenir

 def double_click?(first_click_info, second_click_info) # MouseClickInfo being the parameter object type # having cursor_location and control_at_click as properties 

De telles utilisations vous aideront à découvrir les possibilités d’append un comportement significatif à ces objects parameters. Vous constaterez qu’ils se débarrassent de leur odeur de classe de données initiale plus rapidement à votre confort. : -)