web-dev-qa-db-fra.com

Utilisez la méthode constructeur ou setter?

Je travaille sur un code de l'interface utilisateur où j'ai une classe Action, quelque chose comme ça -

public class MyAction extends Action {
    public MyAction() {
        setText("My Action Text");
        setToolTip("My Action Tool tip");
        setImage("Some Image");
    }
}

Lorsque cette classe d'action a été créée, il a été pratiquement supposé que la classe Action _ ne sera pas personnalisable (dans un sens - son texte, son total ou son image ne sera modifiée nulle part dans le code). Maintenant, nous avons besoin de modifier le texte d'action à un emplacement dans le code. Donc, j'ai suggéré à mon collègue de supprimer le texte d'action codé du constructeur et de l'accepter comme un argument, de sorte que tout le monde soit obligé de transmettre le texte d'action. Quelque chose comme ce code ci-dessous -

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
}

Cependant, il pense que depuis setText() méthode appartient à la classe de base, il peut être utilisé de manière flexible pour transmettre le texte d'action dans lequel l'instance d'action est créée. De cette façon, il n'est pas nécessaire de changer la classe MyAction existante. Donc, son code ressemblerait à ceci comme ça.

MyAction action = new MyAction(); //this creates action instance with the hardcoded text
action.setText("User required new action text"); //overwrite the existing text.

Je ne suis pas sûr que c'est un moyen correct de traiter le problème. Je pense que dans l'utilisateur de cas mentionné ci-dessus est de toute façon changer le texte, alors pourquoi ne pas le forcer tout en construisant l'action? Le seul avantage que je vois avec le code d'origine est que l'utilisateur peut créer une classe d'action sans trop penser à définir le texte.

16
zswap

Le seul avantage que je vois avec le code d'origine est que l'utilisateur peut créer une classe d'action sans trop penser à définir le texte.

Ce n'est en fait pas un avantage, à la plupart des fins, c'est un inconvénient et dans les cas restants, j'appellerais cela une cravate. Et si quelqu'un oublie d'appeler colletext () après la construction? Et si tel est le cas dans un cas inhabituel, peut-être un gestionnaire d'erreur? Si vous souhaitez vraiment forcer le texte à définir, vous devez le forcer au moment de la compilation, car seules les erreurs de compilation de compilation sont en réalité fatal. Tout ce qui se passe au moment de l'exécution dépend de ce chemin de code particulier exécuté.

Je vois deux chemins clairs en avant:

  1. Utilisez un paramètre de constructeur, comme vous le suggérez. Si vous voulez vraiment, vous pouvez passer null _ ou une chaîne vide, mais le fait que vous n'abandonnez pas de texte est explicite plutôt que implicite. Il est facile de voir l'existence d'un paramètre null et de voir qu'il y avait probablement certains pensée, mais pas si facile de voir le manque d'appel et de déterminer si le L'absence de telle était intentionnelle ou non. Pour un cas simple comme celui-ci, c'est probablement l'approche que je prendrais.
  2. Utilisez un motif usine/constructeur. Cela peut être surchargé pour un scénario aussi simple, mais dans un cas plus général, il est très flexible car il vous permet de définir n'importe quel nombre de paramètres et de vérifier les conditions préalables avant ou pendant l'instanciation d'objet (si la construction de l'objet est une opération importante et///Ou la classe peut être utilisée de plus d'une manière, cela peut être un énorme avantage). En particulier dans Java C'est également un idiome courant et suivant des modèles établis dans la langue et le cadre que vous utilisez est très rarement une mauvaise chose.
15
a CVn

La surcharge du constructeur serait une solution simple et directe ici:

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
    public MyAction() {
        this("My Action Text");
    }
}

C'est mieux que d'appeler .setText Plus tard, parce que cette façon dont rien ne doit être écrasé, actionText peut être la chose prévue dès le début.

Au fur et à mesure que votre code évolue et que vous aurez besoin d'une plus grande flexibilité (ce qui se produira sûrement), vous bénéficierez du modèle d'usine/générateur suggéré par une autre réponse.

10
janos

Ajoutez une méthode fluide 'colletext':

public class MyAction ... {
  ...
  public MyAction setText(String text) { ... ; return this; }
}

MyAction a = new MyAction().setText("xxx");

Qu'est-ce qui pourrait être plus clair que ça? Si vous décidez d'ajouter une autre propriété personnalisable, aucun problème.

6
kevin cline

Les conseils d'utilisation des constructeurs ou des constructeurs conviennent parfaitement en général, mais dans mon expérience, manque des points clés pour les actions, qui

  1. Peut-être besoin d'être internationalisé
  2. Sont susceptibles de commercialiser de changer à la dernière minute.

Je suggère fortement que le nom, l'info-bulle, l'icône, etc. Soyez lus à partir d'un fichier de propriétés, de XML, etc. Par exemple, pour l'action Open File-Open, vous pourriez passer dans une propriété et elle chercherait

File.open.name=Open
File.open.tooltip=Open a file
File.open.icon=somedir/open.jpg

C'est un format assez facile à traduire en français, pour essayer une nouvelle meilleure icône, etc. sans temps de programmeur ni une recompilation.

Ceci est juste un contour approximatif, beaucoup est laissé au lecteur ... Recherchez d'autres exemples d'internationalisation.

1
user949300

Tout comme - cline Kevin dit dans sa réponse, je pense que la voie à suivre est de créer un API fluide. Je voudrais juste ajouter que l'API fluide fonctionne mieux lorsque vous avez plus d'une propriété que vous pouvez utiliser.

Il rendra votre code plus lisible et de mon point de vue plus facile et, aham , "sexy" à écrire.

Dans votre cas, cela irait comme ça (désolé pour une faute de frappe, cela a été un an depuis que j'ai écrit mon dernier Java Program):

 public class MyAction extends Action {
    private String _text     = "";
    private String _tooltip  = "";
    private String _imageUrl = "";

    public MyAction()
    {
       // nothing to do here.
    }

    public MyAction text(string value)
    {
       this._text = value;
       return this;
    }

    public MyAction tooltip(string value)
    {
       this._tooltip = value;
       return this;
    }

    public MyAction image(string value)
    {
       this._imageUrl = value;
       return this;
    }
}

Et l'utilisation serait comme ceci:

MyAction action = new MyAction()
    .text("My Action Text")
    .tooltip("My Action Tool tip")
    .image("Some Image");
1
Machado

Pensez à la manière dont les instances seront utilisées et utilisez une solution qui guidera, voire forcera, les utilisateurs à utiliser ces cas de droite, ou au moins mieux. Un programmeur utilisant cette classe aura beaucoup d'autres choses à craindre et à penser. Cette classe ne doit pas ajouter à la liste.

Par exemple, si la classe Myaction est censée être immuable après la construction (et éventuellement une autre initialisation), il ne doit pas avoir de méthode de setter. Si la plupart du temps, il utilisera le "mon texte d'action" par défaut, il devrait y avoir un constructeur sans paramètre, plus un constructeur qui permet un texte optionnel. Maintenant, l'utilisateur n'a pas besoin de penser à utiliser la classe correctement 90% du temps. Si l'utilisateur habituellement devrait Pour donner une pensée au texte, ignorez le constructeur sans paramètre. Maintenant, l'utilisateur est obligé de penser si nécessaire et ne peut pas négliger une étape nécessaire.

Si une instance MyAction doit être mutable après la construction complète, vous avez besoin d'un setter pour le texte. Il est tentant de sauter définir la valeur dans le constructeur (principe sec - "Ne répétez pas vous-même") et, si la valeur par défaut est généralement assez bonne, je le ferais. Mais si ce n'est pas le cas, obliger le texte dans le constructeur oblige l'utilisateur à penser quand ils le devraient.

Notez que ces utilisateurs ne sont pas stupides. Ils ont juste trop de problèmes réels à craindre. En réfléchissant à la "interface" de votre classe, vous pouvez également empêcher de devenir un problème réel - et une inutile.

0
RalphChapin

Dans la solution proposée ci-après, la Superclass est abstraite et dispose des trois membres de la valeur par défaut.

La sous-classe a des constructeurs différents afin que le programmeur puisse l'instancer.

Si le premier constructeur est utilisé, tous les membres auront les valeurs par défaut.

Si le deuxième constructeur est utilisé, vous donnez une valeur initiale à l'élément d'actionText, laissant les deux autres membres avec la valeur par défaut ...

Si le troisième constructeur est utilisé, vous l'instanciez avec une nouvelle valeur pour ActionText et ToolTip, laissant ImageURL Whith la valeur par défaut ...

Etc.

public abstract class Action {
    protected String text = "Default action text";
    protected String toolTip = "Default action tool tip";
    protected String imageURl = "http://myserver.com/images/default.png";

    .... rest of code, I guess setters and getters
}

public class MyAction extends Action {


    public MyAction() {

    }

    public MyAction(String actionText) {
        setText(actionText);
    }

    public MyAction(String actionText, String toolTip_) {
        setText(actionText);
        setToolTip(toolTip_);   
    }

    public MyAction(String actionText, String toolTip_; String imageURL_) {
        setText(actionText);
        setToolTip(toolTip_);
        setImageURL(imageURL_);
    }


}
0
Tulains Córdova

Il est inutile d'appeler Settext (ActionText) ou de SetooltiP ("Mon outil d'action") à l'intérieur du constructeur; Il est plus facile (et vous gagnez plus de performances) si vous initialisez simplement le champ correspondant directement:

    public MyAction(String actionText) {
        this.actionText = actionText;
    }

Si vous changez d'actionText pendant la durée de vie de l'objet correspondant de myAction, vous devez placer une méthode de setter; Si vous n'initialisez pas le champ uniquement dans le constructeur sans fournir une méthode de setter.

Depuis que l'info-bulle et l'image sont des constantes, traitez-les comme des constantes; avoir des champs:

private (or even public) final static String TOOLTIP = "My Action Tooltip";

En fait, lors de la conception d'objets communs (non de haricots ou d'objets représentant strictement des structures de données), il est une mauvaise idée de fournir des setters et des getters, car ils sont un type d'encapsulation de pause.

0
m3th0dman

Je pense que cela est vrai si nous allons faire une classe d'action générique (comme la mise à jour, utilisée pour mettre à jour les employés, le département ...). Tout dépend du scénario. Si une classe d'action spécifique (comme des employés de mise à jour) (utilisait de nombreux emplacements d'application - Employé de mise à jour) est créée avec l'intention de conserver le même texte, la même info-bulle et l'image à chaque endroit de l'application (pour le point de vue de la cohérence). Donc, le codage rigide peut être effectué pour le texte, l'info-bulle et l'image pour fournir le texte, l'info-bulle et l'image par défaut. Toujours donner plus de flexibilité, pour les personnaliser, il devrait avoir des méthodes de réglage correspondantes. Garder dans l'esprit seulement 10% des endroits où nous devons le changer. Prendre le texte de l'action Chaque fois que l'utilisateur peut causer un texte différent à chaque fois pour la même action. Comme 'Mettre à jour EMP', "Mettre à jour l'employé", "changer d'employé" ou "Edit Employee". Je ne suis pas sûr, mais je pense que cela peut créer la confusion à l'utilisateur que c'est quelque chose de différent.

0
Sandy