Constructeur avec toutes les propriétés de la classe ou constructeur par défaut avec les setters?

StackOverflow https://stackoverflow.com/questions/830657

  •  06-07-2019
  •  | 
  •  

Question

Les deux approches suivantes sont les suivantes:

  • constructeur avec toutes les propriétés de classe

Avantages: je dois mettre un nombre exact de types de paramètres, donc si je fais une erreur, le compilateur me le signale (à propos, y a-t-il un moyen d'éviter le problème d'avoir commuté par erreur deux entiers sur la liste de paramètres? )

Inconvénients: si j'ai beaucoup de propriétés, la ligne d'instanciation peut devenir très longue et peut s'étendre sur deux lignes ou plus

  • setters et le constructeur vide par défaut

Avantages: je peux voir clairement ce que je suis en train de paramétrer, donc si je fais quelque chose de mal, je peux le localiser dès que je le tape (je ne peux pas commettre l’erreur précédente de changer deux variables de même type)

Inconvénients: l’instanciation d’un objet avec beaucoup de propriétés peut prendre plusieurs lignes (je ne sais pas s’il s’agit vraiment d’un con) et si j’oublie de définir une propriété, le compilateur ne dit rien.

Que ferez-vous et pourquoi? Connaissez-vous un motif de lumière (pensez-vous qu'il devrait être utilisé chaque fois qu'un objet avec 7 propriétés ou plus est instancié) à suggérer? Je pose la question parce que j’ai tendance à ne pas aimer les grands constructeurs où je ne peux pas comprendre rapidement où se trouve la variable que je cherche. Par contre, je trouve l’option "set all properties". vulnérable à la perte de certaines propriétés.

N'hésitez pas à argumenter mes hypothèses en pour et en contre car elles ne sont que mes pensées:)

Mise à jour - une question que j'ai trouvée et qui est liée à ceci: Construire de gros objets immuables sans utiliser de constructeurs ayant de longues listes de paramètres

Était-ce utile?

La solution

Vous pouvez regarder le modèle de générateur préconisé par Joshua Bloch et décrit dans Java efficace . Une présentation des principaux points est disponible à l'adresse http: //developers.sun. .com / learning / javaoneonline / 2007 / pdf / TS-2689.pdf ; Nul doute que vous pourriez trouver une meilleure référence.

En gros, vous avez une autre classe, probablement une classe interne, qui fournit des méthodes nommées d'après les propriétés définies et qui renvoient le générateur d'origine afin que vous puissiez chaîner les appels. C'est un morceau de code assez lisible.

Par exemple, supposons que j'ai un simple Message avec quelques propriétés. Le code client qui le construit pourrait utiliser un générateur pour préparer un Message comme suit:

Message message = new Message.Builder()
    .sender( new User( ... ) )
    .recipient( new User( ... ) )
    .subject( "Hello, world!" )
    .text( messageText )
    .build();

Un fragment de Message.Builder peut ressembler à ceci:

public class Builder {

    private User sender = null;
    // Other properties

    public Builder sender( User sender ) {
        this.sender = sender;
        return this;
    }
    // Methods for other properties

    public Message build() {
        Message message = new Message();
        message.setSender( sender );
        // Set the other properties
        return message;
    }

}

Autres conseils

Vous avez manqué le plus grand avantage d'avoir un constructeur avec beaucoup de paramètres: il vous permet de créer des types immuables.

La manière habituelle de créer des types immuables sans une énorme méchanceté de constructeur consiste à avoir un type d'assistance - un constructeur qui conserve les valeurs que vous souhaitez dans votre objet final, construit ensuite l'objet immuable lorsque vous êtes prêt.

Des recherches universitaires récentes (CMU et Microsoft) sur la convivialité des API suggèrent que les constructeurs par défaut avec des setters seraient la voie à suivre en termes de convivialité. Ceci est tiré de "Incidences sur l'utilisation des paramètres obligatoires dans les constructeurs d'objets", et leur utilité. Jeff Stylos et Steven Clarke et a été présenté à la Conférence internationale sur le génie logiciel:

Résumé : La facilité d'utilisation des API est de plus en plus importante pour la productivité des programmeurs. Sur la base de l’expérience d’études sur la convivialité d’API spécifiques, des techniques ont été explorées pour étudier la convivialité des choix de conception communs à de nombreuses API. Une étude comparative a été réalisée pour évaluer la manière dont les programmeurs professionnels utilisent les API avec les paramètres requis dans les constructeurs des objets, par opposition aux paramètres "par défaut" sans paramètre. constructeurs. Il était supposé que les paramètres requis créeraient davantage d'API utilisables et auto-documentés en guidant les programmeurs vers l'utilisation correcte des objets et en prévenant les erreurs. Cependant, dans l’étude, il a été constaté que, contrairement aux attentes, les programmeurs préféraient et étaient plus efficaces avec des API qui ne nécessitaient pas de paramètres de constructeur. Le comportement des participants a été analysé à l'aide du cadre des dimensions cognitives et a révélé que les paramètres de constructeur requis interféraient avec les stratégies d'apprentissage courantes, entraînant un engagement prématuré indésirable.

Vous en parlez dans votre article, mais je pense que c'est un point important qui mérite plus d'attention: à moins que chaque paramètre d'entrée ne soit d'un type différent, le gros problème des constructeurs énormes est qu'il est très transposer quelques variables. Le compilateur est un filet de sécurité peu fiable - il détectera quelques erreurs, mais celles qui passeront seront beaucoup plus difficiles à identifier et à déboguer. Surtout parce que la liste d'entrées d'un constructeur énorme est assez opaque à moins que l'API ne soit ouverte dans une autre fenêtre.

Les getters et les setters sont beaucoup plus faciles à déboguer, en particulier si vous instaurez des sauvegardes qui jettent une exception d'exécution si l'objet n'est pas correctement rempli. Et je suis un grand fan de "facile à déboguer".

Avant ce fil de discussion, je n’avais jamais entendu parler du modèle Builder mentionné par Rob. Jamais utilisé moi-même (évidemment), mais c'est sacrément intriguant.

Je préfère prendre des arguments de constructeur, pour les raisons d’immutabilité susmentionnées. Si cela vous donne un constructeur qui prend beaucoup d'arguments (disons plus de quatre ou plus), c'est une odeur de code pour moi: certains de ces arguments devraient être regroupés dans leurs propres types.

Par exemple, si vous avez quelque chose comme ceci:

class Contact
{
    public Contact(string firstName, string lastName, string phoneNumber,
        string street, string city, string state, int zipCode) { ... }
}

Je le refacturerais à:

class Contact
{
    public Contact(Person person, PhoneNumber number, Address address) { ... }
}

class Person
{
    public Person(string firstName, string lastName) { ... }
}

class PhoneNumber
{
    public PhoneNumber(string digits) { ... }
}

class Address
{
    public Address(string street, string city, string state, int zipCode) { ... }
}

Les classes trop grandes constituent un problème de conception très courant dans les bases de code de programmation orientée objet.

Il y a aussi d'autres aspects. Si vous souhaitez pouvoir utiliser certaines classes avec votre classe au moment de la conception plutôt que juste au moment de l'exécution, par exemple en ajoutant votre classe en tant qu'objet dans la palette d'objets (Java utilisant Netbeans), vous devez fournir un constructeur sans argument afin de pouvoir le faire.

Il existe également d'autres stratégies. Avant d'essayer de comprendre comment traiter de nombreux paramètres, je pense qu'il est important de revoir votre conception et de voir si votre classe en fait trop. Voyez si vous pouvez regrouper certains paramètres dans une nouvelle classe et déplacer un comportement dans cette classe.

  

setters et le constructeur vide par défaut

JRL l'a touchée de manière oblique , mais l'une des raisons d'envisager l'utilisation de paramètres est que l'objet soit conforme à la Spécification JavaBean . Cela rend les instances susceptibles de modification via des outils d’introspection et de persistance en utilisant certaines techniques de sérialisation .

Qui a dit que vous ne pouviez pas faire les deux? Je dirais que les propriétés obligatoires vont dans le constructeur, les optionnelles sont gérées avec des setters. BTW, qui dit que vous avez toujours besoin d'un poseur par propriété? Si deux propriétés appartiennent conceptuellement, pourquoi ne pas les associer?

J'aime aussi le motif Constructeur, mais la règle la plus importante est: utilisez toujours votre cerveau et trouvez le motif qui vous convient le mieux. Il n'y a pas de solution unique.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top