Nous avons beaucoup d'endroits dans le code source de notre application, où une classe a de nombreuses méthodes avec les mêmes noms et différents paramètres. Ces méthodes ont toujours tous les paramètres d'une méthode «précédente» plus un de plus.
C'est le résultat d'une longue évolution (code hérité) et de cette réflexion (je crois):
" Il y a une méthode M qui fait la chose A. J'ai besoin de faire A + B. OK, je sais ... Je vais ajouter un nouveau paramètre à M, créer une nouvelle méthode pour cela, déplacer le code de M vers la nouvelle méthode avec un paramètre de plus, faites l'A + B là-bas et appelez la nouvelle méthode à partir de M avec une valeur par défaut du nouveau paramètre. "
Voici un exemple (en langage Java):
class DocumentHome {
(...)
public Document createDocument(String name) {
// just calls another method with default value of its parameter
return createDocument(name, -1);
}
public Document createDocument(String name, int minPagesCount) {
// just calls another method with default value of its parameter
return createDocument(name, minPagesCount, false);
}
public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
// just calls another method with default value of its parameter
return createDocument(name, minPagesCount, false, "");
}
public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
// here the real work gets done
(...)
}
(...)
}
J'ai l'impression que c'est faux. Non seulement nous ne pouvons pas continuer à ajouter de nouveaux paramètres comme celui-ci pour toujours, mais le code est difficile à étendre / modifier en raison de toutes les dépendances entre les méthodes.
Voici quelques façons de mieux faire cela:
Introduisez un objet paramètre:
class DocumentCreationParams { String name; int minPagesCount; boolean firstPageBlank; String title; (...) } class DokumentHome { public Document createDocument(DocumentCreationParams p) { // here the real work gets done (...) } }
Définissez les paramètres de l'
DocumentHome
objet avant d'appelercreateDocument()
@In DocumentHome dh = null; (...) dh.setName(...); dh.setMinPagesCount(...); dh.setFirstPageBlank(...); Document newDocument = dh.createDocument();
Séparez le travail en différentes méthodes et appelez-les selon vos besoins:
@In DocumentHome dh = null; Document newDocument = dh.createDocument(); dh.changeName(newDocument, "name"); dh.addFirstBlankPage(newDocument); dh.changeMinPagesCount(new Document, 10);
Mes questions:
- Le problème décrit est-il vraiment un problème?
- Que pensez-vous des solutions suggérées? Laquelle préféreriez-vous (en fonction de votre expérience)?
- Pouvez-vous penser à une autre solution?
Réponses:
Essayez peut-être le modèle de constructeur ? (note: résultat Google assez aléatoire :)
Je ne peux pas vous expliquer pourquoi je préfère le constructeur aux options que vous donnez, mais vous avez identifié un gros problème avec beaucoup de code. Si vous pensez que vous avez besoin de plus de deux paramètres pour une méthode, votre code est probablement mal structuré (et certains en diront un!).
Le problème avec un objet params est (sauf si l'objet que vous créez est en quelque sorte réel) vous poussez simplement le problème d'un niveau, et vous vous retrouvez avec un groupe de paramètres non liés formant `` l'objet ''.
Vos autres tentatives me ressemblent à quelqu'un qui cherche le modèle de constructeur mais qui n'y arrive pas tout à fait :)
la source
DocumentoFactory
). Avoir unbuilder
sur différents endroits, il est difficile de contrôler les modifications futures de la construction du document (comme ajouter un nouveau champ obligatoire au document, par exemple) et ajouter du code passe-partout supplémentaire sur les tests pour simplement satisfaire les besoins du générateur de documents sur les classes qui utilisent le générateur.L'utilisation d'un objet paramètre est un bon moyen d'éviter une surcharge (excessive) des méthodes:
Je n'irais cependant pas trop loin.
Avoir une surcharge ici et là n'est pas une mauvaise chose. Il est pris en charge par le langage de programmation, alors utilisez-le à votre avantage.
Je n'étais pas au courant du modèle de constructeur, mais je l'ai utilisé "par accident" à quelques reprises. Il en va de même ici: n'en faites pas trop. Le code de votre exemple en bénéficierait, mais passer beaucoup de temps à l'implémenter pour chaque méthode qui a une seule méthode de surcharge n'est pas très efficace.
Juste mes 2 cents.
la source
Honnêtement, je ne vois pas de gros problème avec le code. En C # et C ++, vous pouvez utiliser des paramètres facultatifs, ce serait une alternative, mais pour autant que je sache, Java ne prend pas en charge ce type de paramètres.
En C #, vous pouvez rendre toutes les surcharges privées et une méthode avec des paramètres facultatifs est publique pour appeler le truc.
Pour répondre à votre question partie 2, je prendrais l'objet paramètre ou même un dictionnaire / HashMap.
Ainsi:
Comme avertissement, je suis d'abord un programmeur C # et JavaScript, puis un programmeur Java.
la source
Je pense que c'est un bon candidat pour le modèle de constructeur. Le modèle de générateur est utile lorsque vous souhaitez créer des objets du même type, mais avec des représentations différentes.
Dans votre cas, j'aurais un constructeur avec les méthodes suivantes:
Vous pouvez ensuite utiliser le générateur de la manière suivante:
D'un autre côté, cela ne me dérange pas de quelques surcharges simples - mais que diable, le framework .NET les utilise partout avec des aides HTML. Cependant, je réévaluerais ce que je fais si je dois passer plus de deux paramètres à chaque méthode.
la source
Je pense que la
builder
solution peut fonctionner sur la plupart des scénarios, mais sur des cas plus complexes, votre constructeur sera également complexe à configurer , car il est facile de commettre des erreurs dans l'ordre des méthodes, quelles méthodes doivent être exposées ou non , etc. Ainsi, beaucoup d'entre nous préféreront une solution la plus simple.Si vous venez de créer un simple générateur pour construire un document et diffuser ce code sur différentes parties (classes) de l'application, il sera difficile de:
Mais cela ne répond pas à la question OP.
L'alternative à la surcharge
Quelques alternatives:
createDocument
, comme:createLicenseDriveDocument
,createDocumentWithOptionalFields
, etc. Bien sûr, cela peut vous conduire à des noms de méthode géants, donc ce n'est pas une solution pour tous les cas.Document
, comme:Document.createLicenseDriveDocument()
.createDocument(InterfaceDocument interfaceDocument)
et créer différentes implémentations pourInterfaceDocument
. Par exemple:createDocument(new DocumentMinPagesCount("name"))
. Bien sûr, vous n'avez pas besoin d'une seule implémentation pour chaque cas, car vous pouvez créer plus d'un constructeur sur chaque implémentation, en regroupant certains champs qui ont du sens sur cette implémentation. Ce modèle est appelé constructeurs télescopiques .Ou restez simplement avec une solution de surcharge . Même étant, parfois, une solution laide, il n'y a pas beaucoup d'inconvénients à l'utiliser. Dans ce cas, je préfère utiliser des méthodes de surcharge sur une classe séparée, comme une
DocumentoFactory
qui peut être injectée comme dépendance sur des classes qui ont besoin de créer des documents. Je peux organiser et valider les champs sans la complexité de créer un bon constructeur et de maintenir le code sur un seul endroit aussi.la source