Question

Est-ce complètement contraire à la manière Java de créer des objets de type structure ?

class SomeData1 {
    public int x;
    public int y;
}

Je peux voir une classe avec des accesseurs et des mutateurs ressemblant davantage à Java.

class SomeData2 {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    private int x;
    private int y;
}

La classe du premier exemple est notoirement pratique.

// a function in a class
public int f(SomeData1 d) {
    return (3 * d.x) / d.y;
}

Ce n'est pas aussi pratique.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}
Était-ce utile?

La solution

C’est un sujet couramment discuté.L'inconvénient de la création de champs publics dans des objets est que vous n'avez aucun contrôle sur les valeurs qui leur sont définies.Dans les projets de groupe où de nombreux programmeurs utilisent le même code, il est important d'éviter les effets secondaires.En outre, il est parfois préférable de renvoyer une copie de l'objet du champ ou de le transformer d'une manière ou d'une autre, etc.Vous pouvez vous moquer de ces méthodes dans vos tests.Si vous créez une nouvelle classe, vous ne verrez peut-être pas toutes les actions possibles.C'est comme une programmation défensive - un jour, les getters et setters peuvent être utiles, et leur création/utilisation ne coûte pas cher.Ils sont donc parfois utiles.

En pratique, la plupart des champs ont des getters et setters simples.Une solution possible ressemblerait à ceci :

public property String foo;   
a->Foo = b->Foo;

Mise à jour:Il est très peu probable que la prise en charge des propriétés soit ajoutée dans Java 7 ou peut-être jamais.D'autres langages JVM comme Groovy, Scala, etc. prennent désormais en charge cette fonctionnalité.-Alex Miller

Autres conseils

Il semble que de nombreux personnes Java ne connaissent pas les directives de codage Sun Java qui disent qu'il est tout à fait approprié d'utiliser la variable d'instance publique lorsque la classe est essentiellement une "structure", si Java a pris en charge la "structure" (lorsqu'il n'y a pas de comportement).

Les gens ont tendance à penser que les getters et les setters sont la voie Java, comme s'ils étaient au cœur de Java.Ce n’est pas le cas.Si vous suivez les directives de codage Sun Java, en utilisant des variables d'instance publique dans des situations appropriées, vous écrivez en fait un meilleur code que de les encombrer avec des getters et des setters inutiles.

Conventions de code Java de 1999 et toujours inchangé.

10.1 Fournir l'accès aux variables d'instance et de classe

Ne rendez publique aucune variable d’instance ou de classe sans raison valable.Souvent, les variables d'instance n'ont pas besoin d'être explicitement définies ou obtenues souvent, ce qui se produit comme effet secondaire des appels de méthode.

Un exemple de variables d'instance publiques appropriées est le cas où la classe est essentiellement une structure de données, sans comportement. En d'autres termes, si vous auriez utilisé une structure au lieu d'une classe (si la structure est prise en charge par Java), alors il est approprié de rendre publiques les variables d'instance de la classe..

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28

Faites vraiment preuve de bon sens.Si vous avez quelque chose comme :

public class ScreenCoord2D{
    public int x;
    public int y;
}

Ensuite, cela ne sert à rien de les envelopper dans des getters et des setters.Vous n’allez jamais stocker une coordonnée x, y en pixels entiers d’une autre manière.Les getters et setters ne feront que vous ralentir.

Par contre avec :

public class BankAccount{
    public int balance;
}

Vous souhaiterez peut-être modifier la façon dont un solde est calculé à un moment donné dans le futur.Cela devrait vraiment utiliser des getters et des setters.

C'est toujours préférable de savoir pourquoi vous appliquez de bonnes pratiques, afin de savoir quand il est acceptable de contourner les règles.

Pour résoudre les problèmes de mutabilité, vous pouvez déclarer x et y comme définitifs.Par exemple:

class Data {
  public final int x;
  public final int y;
  public Data( int x, int y){
    this.x = x;
    this.y = y;
  }
}

L'appel du code qui tente d'écrire dans ces champs entraînera une erreur de compilation du type "le champ x est déclaré final ;ne peut être cédé".

Le code client peut alors avoir la commodité « abrégée » que vous avez décrite dans votre message

public class DataTest {
    public DataTest() {
        Data data1 = new Data(1, 5);
        Data data2 = new Data(2, 4);
        System.out.println(f(data1));
        System.out.println(f(data2));
    }

    public int f(Data d) {
        return (3 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}

Ne pas utiliser public des champs

Ne pas utiliser public champs lorsque vous voulez vraiment envelopper le comportement interne d’une classe.Prendre java.io.BufferedReader Par exemple.Il comporte le champ suivant :

private boolean skipLF = false; // If the next character is a line feed, skip it

skipLF est lu et écrit dans toutes les méthodes de lecture.Que se passe-t-il si une classe externe exécutée dans un thread distinct modifie de manière malveillante l'état de skipLF en pleine lecture ? BufferedReader va certainement se détraquer.

Utilisez public des champs

Prends ça Point classe par exemple :

class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return this.x;
    }

    public double getY() {
        return this.y;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }
}

Cela rendrait le calcul de la distance entre deux points très pénible à écrire.

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2));

La classe n'a pas de comportement autre que des getters et des setters simples.Il est acceptable d'utiliser des champs publics lorsque la classe représente simplement une structure de données et n'a pas, et n'aura jamais de comportement (les getters et setters fins sont pas comportement considéré ici).Cela peut être mieux écrit de cette façon :

class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

Faire le ménage!

Mais rappelles-toi:Non seulement votre classe doit être dépourvue de comportement, mais elle doit également avoir Non raison d'avoir un comportement à l'avenir également.


(C'est exactement ce que cette réponse décrit.Citer "Conventions de code pour le langage de programmation Java :dix.Pratiques de programmation":

Un exemple de variables d'instance publiques appropriées est le cas où la classe est essentiellement une structure de données, sans comportement.En d'autres termes, si vous aviez utilisé un struct au lieu d'une classe (si Java est pris en charge struct), alors il convient de rendre publiques les variables d'instance de la classe.

La documentation officielle accepte donc également cette pratique.)


De plus, si vous êtes absolument sûr que les membres ci-dessus Point la classe doit être immuable, alors vous pouvez ajouter final mot-clé pour l'appliquer :

public final double x;
public final double y;

À propos, la structure que vous donnez comme exemple existe déjà dans la bibliothèque de classes de base Java sous la forme java.awt.Point.Il a x et y comme champs publics, Vérifiez par vous-même.

Si vous savez ce que vous faites et que d'autres membres de votre équipe le savent, alors vous pouvez avoir des champs publics.Mais vous ne devriez pas vous y fier car ils peuvent causer des maux de tête comme dans les bugs liés aux développeurs utilisant des objets comme s'il s'agissait de structures allouées à la pile (les objets Java sont toujours envoyés aux méthodes comme références et non comme copies).

Concernant:aku, izb, John Topley...

Attention aux problèmes de mutabilité...

Il peut sembler judicieux d'omettre les getters/setters.En fait, cela peut convenir dans certains cas.Le vrai problème avec le modèle proposé présenté ici est la mutabilité.

Le problème est que vous transmettez une référence d'objet contenant des champs publics non finaux.Tout ce qui contient cette référence est libre de modifier ces champs.Vous n'avez plus aucun contrôle sur l'état de cet objet.(Pensez à ce qui se passerait si les chaînes étaient mutables.)

Cela devient mauvais lorsque cet objet est une partie importante de l'état interne d'un autre, vous venez d'exposer l'implémentation interne.Pour éviter cela, une copie de l'objet doit être renvoyée à la place.Cela fonctionne, mais peut entraîner une pression massive sur le GC en raison de tonnes de copies à usage unique créées.

Si vous disposez de champs publics, envisagez de rendre la classe en lecture seule.Ajoutez les champs comme paramètres au constructeur et marquez les champs comme finaux.Sinon, assurez-vous de ne pas exposer l'état interne et si vous devez construire de nouvelles instances pour une valeur de retour, assurez-vous qu'elle ne sera pas appelée de manière excessive.

Voir:"Java efficace" par Joshua Bloch -- Article n°13 :Privilégiez l’immuabilité.

PS :Gardez également à l’esprit que toutes les JVM de nos jours optimiseront si possible la getMethod, ce qui entraînera une seule instruction de lecture de champ.

J'ai essayé cela dans quelques projets, sur la théorie selon laquelle les getters et les setters encombrent le code avec des éléments grossiers sémantiquement dénués de sens, et que d'autres langages semblent très bien se débrouiller avec le masquage des données ou le partitionnement des responsabilités basé sur des conventions (par ex.python).

Comme d'autres l'ont noté ci-dessus, vous rencontrez 2 problèmes, et ils ne peuvent pas vraiment être résolus :

  • Presque tous les outils automatisés du monde Java s'appuient sur la convention getter/setter.Idem pour, comme l'ont noté d'autres, les balises jsp, la configuration Spring, les outils Eclipse, etc.etc...Lutter contre ce que vos outils s'attendent à voir est une recette pour de longues sessions de recherche sur Google pour essayer de trouver cette manière non standard d'initier les haricots de printemps.Cela n'en vaut vraiment pas la peine.
  • Une fois que vous avez votre application élégamment codée avec des centaines de variables publiques, vous trouverez probablement au moins une situation où elles sont insuffisantes - où vous avez absolument besoin d'immuabilité, ou vous devez déclencher un événement lorsque la variable est définie, ou vous souhaitez lancer une exception sur un changement de variable car elle définit l'état d'un objet sur quelque chose de désagréable.Vous êtes alors coincé avec des choix peu enviables entre encombrer votre code avec une méthode spéciale partout où la variable est directement référencée, ou avoir un formulaire d'accès spécial pour 3 des 1000 variables de votre application.

Et dans le meilleur des cas, il s’agit de travailler entièrement dans un projet privé autonome.Une fois que vous aurez exporté le tout vers une bibliothèque accessible au public, ces problèmes deviendront encore plus importants.

Java est très verbeux, et c'est une chose tentante à faire.Ne le fais pas.

Si la méthode Java est la méthode OO, alors oui, la création d'une classe avec des champs publics enfreint les principes de masquage des informations qui disent qu'un objet doit gérer son propre état interne.(Donc, comme je ne vous parle pas simplement de jargon, l'un des avantages du masquage d'informations est que le fonctionnement interne d'une classe est caché derrière une interface - disons que vous vouliez changer le mécanisme par lequel votre classe struct a enregistré l'un de ses champs, vous devrez probablement revenir en arrière et modifier toutes les classes qui utilisent la classe...)

Vous ne pouvez pas non plus profiter de la prise en charge des classes compatibles avec la dénomination JavaBean, ce qui vous fera mal si vous décidez, par exemple, d'utiliser la classe dans une page JavaServer écrite en utilisant le langage d'expression.

L'article JavaWorld Pourquoi les méthodes Getter et Setter sont mauvaises Cet article pourrait également vous intéresser pour réfléchir au moment où il ne faut pas implémenter les méthodes d'accès et de mutation.

Si vous écrivez une petite solution et souhaitez minimiser la quantité de code impliquée, la méthode Java n'est peut-être pas la bonne - je suppose que cela dépend toujours de vous et du problème que vous essayez de résoudre.

Il n'y a rien de mal à ce type de code, à condition que l'auteur sait ce sont des structures (ou des navettes de données) au lieu d'objets.De nombreux développeurs Java ne peuvent pas faire la différence entre un objet bien formé (pas seulement une sous-classe de java.lang.Object, mais un objet bien formé). vrai objet dans un domaine spécifique) et un ananas.Ergo, ils finissent par écrire des structures lorsqu'ils ont besoin d'objets et vice versa.

Le problème lié à l'utilisation de l'accès aux champs publics est le même problème que l'utilisation d'une méthode new au lieu d'une méthode d'usine - si vous changez d'avis plus tard, tous les appelants existants sont interrompus.Ainsi, du point de vue de l'évolution de l'API, c'est généralement une bonne idée de serrer les dents et d'utiliser des getters/setters.

Un endroit où je vais dans l'autre sens est lorsque vous contrôlez fortement l'accès à la classe, par exemple dans une classe statique interne utilisée comme structure de données interne.Dans ce cas, il pourrait être beaucoup plus clair d’utiliser l’accès aux champs.

À propos, selon l'affirmation d'e-bartek, il est hautement improbable, à mon avis, que la prise en charge des propriétés soit ajoutée dans Java 7.

J'utilise fréquemment ce modèle lors de la création de classes internes privées pour simplifier mon code, mais je ne recommanderais pas d'exposer de tels objets dans une API publique.En général, plus vous pouvez rendre immuables les objets de votre API publique, mieux c'est, et il n'est pas possible de construire votre objet « de type struct » de manière immuable.

En passant, même si j'écrivais cet objet en tant que classe interne privée, je fournirais toujours un constructeur pour simplifier le code permettant d'initialiser l'objet.Devoir avoir 3 lignes de code pour obtenir un objet utilisable quand on le fera est tout simplement compliqué.

Une très très vieille question, mais permettez-moi d'apporter une autre brève contribution.Java 8 a introduit les expressions lambda et les références de méthodes.Les expressions Lambda peuvent être de simples références de méthode et ne pas déclarer un « vrai » corps.Mais vous ne pouvez pas « convertir » un champ en référence de méthode.Ainsi

stream.mapToInt(SomeData1::x)

ce n'est pas légal, mais

stream.mapToInt(SomeData2::getX)

est.

Je ne vois pas de mal si vous savez que ce sera toujours une structure simple et que vous ne voudrez jamais y attacher de comportement.

Il s'agit d'une question sur la conception orientée objet, et non sur le langage Java.Il est généralement recommandé de masquer les types de données au sein de la classe et d'exposer uniquement les méthodes qui font partie de l'API de la classe.Si vous exposez des types de données internes, vous ne pourrez plus jamais les modifier à l'avenir.Si vous les masquez, votre seule obligation envers l'utilisateur concerne les types de retour et d'argument de la méthode.

Ici, je crée un programme pour saisir le nom et l'âge de 5 personnes différentes et effectuer un tri par sélection (en fonction de l'âge).J'ai utilisé une classe qui fait office de structure (comme le langage de programmation C) et une classe principale pour effectuer l'opération complète.Ci-dessous, je fournis le code...

import java.io.*;

class NameList {
    String name;
    int age;
}

class StructNameAge {
    public static void main(String [] args) throws IOException {

        NameList nl[]=new NameList[5]; // Create new radix of the structure NameList into 'nl' object
        NameList temp=new NameList(); // Create a temporary object of the structure

        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        /* Enter data into each radix of 'nl' object */

        for(int i=0; i<5; i++) {
            nl[i]=new NameList(); // Assign the structure into each radix

            System.out.print("Name: ");
            nl[i].name=br.readLine();

            System.out.print("Age: ");
            nl[i].age=Integer.parseInt(br.readLine());

            System.out.println();
        }

        /* Perform the sort (Selection Sort Method) */

        for(int i=0; i<4; i++) {
            for(int j=i+1; j<5; j++) {
                if(nl[i].age>nl[j].age) {
                    temp=nl[i];
                    nl[i]=nl[j];
                    nl[j]=temp;
                }
            }
        }

        /* Print each radix stored in 'nl' object */

        for(int i=0; i<5; i++)
            System.out.println(nl[i].name+" ("+nl[i].age+")");
    }
}

Le code ci-dessus est sans erreur et testé...Copiez-le et collez-le simplement dans votre IDE et...Tu sais et quoi ???:)

Vous pouvez créer une classe simple avec des champs publics et aucune méthode en Java, mais il s'agit toujours d'une classe et elle est toujours gérée syntaxiquement et en termes d'allocation de mémoire, tout comme une classe.Il n'existe aucun moyen de reproduire véritablement des structures en Java.

Parfois, j'utilise une telle classe lorsque j'ai besoin de renvoyer plusieurs valeurs à partir d'une méthode.Bien sûr, un tel objet est de courte durée et avec une visibilité très limitée, donc ça devrait aller.

Comme pour la plupart des choses, il y a la règle générale et il y a ensuite des circonstances spécifiques.Si vous réalisez une application fermée et capturée afin de savoir comment un objet donné va être utilisé, vous pouvez alors exercer plus de liberté pour privilégier la visibilité et/ou l'efficacité.Si vous développez une classe qui va être utilisée publiquement par d'autres personnes indépendantes de votre volonté, optez pour le modèle getter/setter.Comme pour toute chose, faites simplement preuve de bon sens.Il est souvent acceptable de faire un premier tour avec les publics, puis de les changer plus tard en getter/setters.

La programmation orientée aspect vous permet d'intercepter des affectations ou des récupérations et d'y attacher une logique d'interception, ce qui, selon moi, est la bonne façon de résoudre le problème.(La question de savoir s’ils doivent être publics, protégés ou protégés par paquet est orthogonale.)

Ainsi, vous démarrez avec des champs non interceptés avec le bon qualificatif d'accès.Au fur et à mesure que les exigences de votre programme augmentent, vous attachez une logique pour éventuellement valider, faire une copie de l'objet renvoyé, etc.

La philosophie getter/setter impose des coûts dans un grand nombre de cas simples où ils ne sont pas nécessaires.

Que le style d'aspect soit plus propre ou non est quelque peu qualitatif.Je trouverais facile de voir uniquement les variables d'une classe et de visualiser la logique séparément.En fait, la raison d'être de la programmation orientée Apect est que de nombreuses préoccupations sont transversales et que les compartimenter dans le corps de la classe lui-même n'est pas idéal (la journalisation étant un exemple - si vous souhaitez enregistrer tous les éléments, Java veut que vous le fassiez écrivez tout un tas de getters et gardez-les synchronisés mais AspectJ vous permet une seule ligne).

La question de l’IDE ​​est une fausse piste.Ce n'est pas tant la frappe que la lecture et la pollution visuelle qui découlent des get/sets.

Les annotations semblent à première vue similaires à la programmation orientée aspect, mais elles nécessitent que vous énumériez de manière exhaustive les points de coupe en attachant des annotations, par opposition à une spécification concise de type joker dans AspectJ.

J'espère que la connaissance d'AspectJ empêchera les gens de s'installer prématurément sur des langages dynamiques.

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