Les méthodes d'une classe doivent-elles appeler leurs propres accesseurs et leurs propres régleurs?

53

Là où je travaille, je vois beaucoup de classes qui font des choses comme celle-ci:

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

Si j'avais écrit ça à partir de rien, j'aurais methodWithLogic()plutôt écrit ceci:

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

Je pense que lorsque la classe appelle ses propres accesseurs et régleurs, cela rend le code plus difficile à lire. Pour moi, cela implique presque que de la logique complexe se produit dans cet appel de méthode, même si dans notre cas ce n’est presque jamais. Lorsque je débogue des codes inconnus, qui peut dire que le bogue n’est pas un effet secondaire de cette méthode? En d'autres termes, cela me fait faire beaucoup de détournements pour comprendre le code.

Y a-t-il des avantages à la première méthode? La première méthode est-elle réellement meilleure?

Daniel Kaplan
la source
Lorsque je débogue des codes inconnus, qui peut dire que le bogue n’est pas un effet secondaire de cette méthode? Votre unité teste. :)
Joshua Taylor
1
L'utilisation de getters / setters dans votre code interne vous permet de créer un point d'arrêt dans votre code si vous le parcourez avec un débogueur. Cela vous permet également de vous assurer que si quelque chose ne va pas avec la définition / l'obtention d'une valeur, vous savez que c'est à cause de cette méthode et non à cause d'un accès invalide. Globalement, cela donne plus de code de consistance.
Callyalater

Réponses:

46

Je ne dirai pas ce qui est meilleur ou pire, car cela dépend en partie de votre situation (à mon avis). Mais considérez que vos getters et setters peuvent changer d'implémentation plus tard, et les contourner éviterait cette logique.

Par exemple, que se passe-t-il si vous ajoutez un indicateur "sale" à certains champs de définition ultérieurement? En appelant vos traceurs dans votre code interne, vous définissez le drapeau sale sans changer aucun autre code. Dans de nombreuses situations, ce serait une bonne chose.

Matt S
la source
Mais qu'en est-il lorsque vous initialisez les données dans le back-end et que vous ne voulez pas que cela soit interprété comme sale? Si vous avez appelé setField()depuis le début, vous venez d'introduire un bogue.
Daniel Kaplan
6
Oui, c'est pourquoi je dis que cela dépend en partie de la situation. Peut-être que votre initialisation de données devrait ignorer les paramètres, mais pas l’autre code logique. Choisissez des règles appropriées et respectez-les.
Matt S
3
@DanielKaplan: le fait est qu'appeler des accesseurs et des passeurs est dans la plupart des cas la bonne chose à faire, et pas seulement dans des situations spécifiques. Cependant, dans le code du monde réel, lorsque vous modifiez ultérieurement une implémentation de getter / setter existante pour introduire des effets secondaires intentionnels, vous devrez probablement vérifier chaque appel du getter ou du setter dans la classe, ainsi que tout accès direct à la classe. champ. C'est pourquoi vous devriez essayer de garder vos cours aussi petits que possible.
Doc Brown le
33

Appeler directement le passeur n’est pas un problème en soi. La question devrait vraiment être: pourquoi notre code a-t-il des setters partout?

Les classes mutables sont un jeu dangereux, en particulier lorsque la classe gère elle-même son propre état. Considérez combien de ces setters ont vraiment besoin d'exister. Combien pourraient être définies dans le constructeur et ensuite être entièrement encapsulées par la classe elle-même?

Si le champ doit pouvoir être réglé de manière externe, demandez-vous si la méthode avec la logique devrait être là. Votre classe elle-même est-elle vraiment juste une classe de données / état? Cette classe a-t-elle plus d'une responsabilité?

Ne vous méprenez pas, il y aura des cas où tout va bien. Je ne dis pas que vous devriez éliminer complètement les setters des classes avec logique. Mais ceux-ci devraient être l'exception, pas la règle. Et, dans ces quelques cas, il devrait être évident de savoir à qui accéder directement.

pdr
la source
1
Je suis complètement d'accord / pratique cette ligne de pensée. Je pense aussi que vous soulevez un point qui est en réalité plus important que ma question. Cela dit, je ne pense pas que cela réponde directement à ma question initiale. À moins que vous ne disiez, "dans le grand schéma des choses, votre question n'a pas d'importance". Ce qui peut aussi être vrai :)
Daniel Kaplan
@DanielKaplan: Je dis exactement cela. :) J'étais tiraillé entre la réponse et le commentaire avec celui-ci, mais je ne pense vraiment pas qu'il existe une réponse correcte qui ne pose pas la grande question.
pdr
9

Oui, les méthodes de votre classe devraient appeler les getters et les setters. L’essentiel dans l’écriture des getters et des setters est la pérennité. Vous pouvez transformer chaque propriété en champ et exposer directement les données aux utilisateurs de la classe. La raison pour laquelle vous créez les getters et les setters n’est pas nécessairement due à une logique complexe, mais à ce que l’interface ne se casse pas à l’avenir si vous devez l’ajouter.

Michael
la source
1
Je ne vois pas la relation entre votre première phrase et le reste de votre paragraphe. La première phrase dit qu'une classe devrait appeler ses propres accesseurs et ses propres setters. La suite du paragraphe explique pourquoi le code client (c'est-à-dire: le code utilisant la classe) doit utiliser les accesseurs et les passeurs au lieu d'accéder directement aux champs. Mais je ne vois pas pourquoi c'est pourquoi la classe ne devrait pas accéder directement à ses propres champs.
Daniel Kaplan
1
@DanielKaplan Mon argument est que les raisons sont identiques. Que si vous ajoutez la logique de définition ultérieurement, cela aura un impact autant sur le code interne que sur le code externe (potentiellement).
Michael
2
@Michael: Une classe peut souvent avoir de bonnes raisons de ne pas utiliser ses propres getters / setters. Un scénario courant est celui où il existe de nombreux paramètres du formulaire someField=newValue; refresh(); Si la méthode permet de définir plusieurs champs, appeler des paramètres pour écrire ces champs entraînerait des opérations "d'actualisation" redondantes. Écrire tous les champs puis appeler refresh()une fois peut permettre une opération plus efficace et plus fluide.
Supercat
6

Pour répondre à vos questions en un mot, oui.

Le fait d’avoir une classe appelle ses propres getters et setters ajoute de l’extensibilité et fournit une meilleure base pour le code futur.

Disons que vous avez quelque chose comme ça:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

Appeler les setters pour l'année et la marque actuellement n'ajoute probablement aucune fonctionnalité, mais que se passe-t-il si vous voulez ajouter quelque chose comme une validation d'entrée aux setters?

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}
Zach Latta
la source
5

Une chose qui n’a pas été mentionnée est que le getter et les setters (comme toutes les méthodes) sont virtuels en Java. Cela ajoute une autre fonctionnalité pour toujours les utiliser dans votre code. Un autre utilisateur pourrait étendre votre classe en écrasant vos getters et setters. Votre classe de base utiliserait alors les données de la sous-classe plutôt que les siennes. Dans un langage où vous marquez explicitement les fonctions virtuelles, ceci est beaucoup plus pratique car vous pouvez prédire et déclarer les fonctions avec lesquelles cela peut être fait. En Java, il faut toujours en être conscient. Si c'est le comportement souhaité, alors les utiliser dans votre code est une bonne chose, sinon moins.

Jonathan Henson
la source
3

Si vous essayez d'accomplir quelque chose qui est fourni par l'interface publique, utilisez les accesseurs / régleurs.

Cependant, en tant que propriétaire / développeur de la classe, vous êtes autorisé à accéder aux sections privées de votre code (à partir de la classe, bien sûr), mais vous êtes également responsable d'atténuer les dangers.

Donc, vous avez peut-être un getter qui parcourt une liste interne et vous voulez obtenir la valeur actuelle sans incrémenter l'itérateur. Dans ce cas, utilisez la variable privée.

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}
ConditionRacer
la source
Pouvez-vous donner le raisonnement pour cela?
Daniel Kaplan
1

Oui. Les accesseurs et les préposés représentent l'état, alors changez la question. Souhaitez-vous suivre de nombreuses façons de modifier l'état d'un objet de la même manière, à moins que vous n'ayez à le faire?

Moins vous avez de tâches à suivre, mieux c’est la raison pour laquelle les objets immuables sont plus faciles à gérer.

Considérez que rien ne vous empêche d’avoir à la fois un public et un passeur / passeur public - mais que gagneriez-vous?

Il peut arriver qu’il soit souhaitable, voire nécessaire, d’accéder directement au terrain pour une ou plusieurs raisons, vous ne devriez pas hésiter à le faire si cela se produit. Mais vous ne devriez vraiment le faire que s'il existe un avantage défini à le faire.

jmoreno
la source
Le but de ma question est que je ne parle que d’accès direct aux champs de la même classe. Je comprends déjà l’objet des getters et des setters pour le code client.
Daniel Kaplan
@DanielKaplan: Je comprends cela, et ce que je dis, c'est que, dans la mesure du possible, vous devriez les traiter de la même manière.
Jmoreno
@DanielKaplan: Vous pourriez avoir un passeur privé et un passeur public, qui ont exactement le même résultat (tous les effets secondaires sont identiques) au moins pour l'instant, mais qu'est-ce que cela vous rapporterait d'autre que le potentiel de divergence? La différence entre ce scénario et ce que vous décrivez est que vous ne pouvez pas éviter de pouvoir accéder à l'état en dehors des accesseurs / configurateurs, mais vous pouvez également éviter de le faire. Ne compliquez pas votre code sauf si vous devez le faire.
Jmoreno
1

Les deux méthodes ont leurs cas d'utilisation. Comme le setter public maintient la valeur du champ (et / ou les valeurs liées) cohérente, vous devez utiliser setter lorsque la logique de votre méthode n'interfère pas avec cette logique de cohérence. Si vous venez de définir votre "propriété", utilisez setter. D’autre part, il existe des situations où vous avez besoin d’un accès direct à certains champs, par exemple des opérations en bloc avec un setter lourd ou des opérations dans lesquelles le concept de setter est trop simple.

C'est votre responsabilité de garder les choses cohérentes. Setter le fait par définition, mais ne peut pas couvrir des cas complexes.

Artur
la source
1

Je dirais non ..

Si vos getters / setters récupèrent et définissent (pas d'initialisation lazy, pas de contrôles, etc.), utilisez simplement vos variables. Si, à l'avenir, vous modifiez vos accesseurs / configurateurs, vous pouvez très bien changer votre methodWithLogic()(car c'est votre classe en train de changer) et vous pouvez appeler un acquéreur / configurateur au lieu d'une affectation directe. Vous pouvez documenter cet appel pour le getter / setter (car il sera étrange d'appeler un getter / setter lorsque le reste du code de votre classe utilise directement la variable).

La machine virtuelle Java appellera fréquemment les getters / setters en ligne. Donc, ce n'est jamais un problème de performance. Le gain de l’utilisation des variables est la lisibilité et à mon humble avis, j’y retournerais.

J'espère que cela t'aides..

Pravin Sonawane
la source