Strukturieren Sie ähnliche Objekte in Java


195

Ist es völlig gegen die Java-Methode, strukturähnliche Objekte zu erstellen?

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

Ich kann eine Klasse mit Accessoren und Mutatoren sehen, die eher Java ähneln.

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

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

    private int x;
    private int y;
}

Die Klasse aus dem ersten Beispiel ist notatorisch praktisch.

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

Dies ist nicht so bequem.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}

9
Betrachten Sie anstelle von öffentlichen veränderlichen Feldern entweder öffentliche unveränderliche Felder oder paketlokale veränderbare Felder. Beides wäre meiner Meinung nach besser.
Peter Lawrey

Denken Sie daran, dass die Getter und Setter zwar hässlich / ausführlich sind, dies jedoch das Herz von Java bildet. Es ist eine nicht prägnante Sprache. Auf der anderen Seite sollten Sie jedoch NIEMALS etwas davon eingeben, da Ihre IDE dies für Sie tut. In einer dynamischen Sprache müssen Sie weniger eingeben, aber Sie müssen eingeben (im Allgemeinen, obwohl IDEs hilfreich sein können).
Dan Rosenstark

Obwohl OO seine Stärken in Bezug auf die Kapselung hat, muss ironischerweise ein Preis für die CPU und den Speicher gezahlt werden. Der Garbage Collector beseitigt (fast vollständig) die Notwendigkeit, sich Gedanken darüber zu machen, wann Objektreferenzen gelöscht werden sollen. Der aktuelle Trend schließt sich durch die Verwendung von C-ähnlichen Strukturen außerhalb des Haufens. Dies ist perfekt für Caching-Lösungen, prozessübergreifende Kommunikation, schnellere speicherintensive Vorgänge, geringere GC-O / h und kann sogar von einer geringeren Speicher-O / h für Ihre Datensätze profitieren. Wenn Sie wissen, was Sie tun, würden Sie diese Frage nicht stellen ... denken Sie also noch einmal darüber nach!
user924272

@ user924272: Re "Der aktuelle Trend schließt sich durch die Verwendung von C-ähnlichen Strukturen außerhalb des Heap". Was würdest du in Java machen wie ??? IMHO, dies ist ein Bereich, in dem Java sein Alter zeigt ...
ToolmakerSteve

@ToolmakerSteve -Ich sehe einen Kreis. Ich bin nicht der einzige. Unternehmen wie Azul sind scharf auf pausenlose Müllabfuhr. Java ist alt. Wahr. Ingenieure, die eine Schwäche erkennen und etwas dagegen tun, anstatt zu stöhnen? Sie verdienen Respekt! +10 zu Azul von mir :-)
user924272

Antworten:


62

Dies ist ein häufig diskutiertes Thema. Der Nachteil beim Erstellen öffentlicher Felder in Objekten besteht darin, dass Sie keine Kontrolle über die darauf festgelegten Werte haben. In Gruppenprojekten, in denen viele Programmierer denselben Code verwenden, ist es wichtig, Nebenwirkungen zu vermeiden. Außerdem ist es manchmal besser, eine Kopie des Feldobjekts zurückzugeben oder es irgendwie zu transformieren usw. Sie können solche Methoden in Ihren Tests verspotten. Wenn Sie eine neue Klasse erstellen, werden möglicherweise nicht alle möglichen Aktionen angezeigt. Es ist wie defensives Programmieren - eines Tages können Getter und Setter hilfreich sein, und es kostet nicht viel, sie zu erstellen / zu verwenden. Sie sind also manchmal nützlich.

In der Praxis haben die meisten Felder einfache Getter und Setter. Eine mögliche Lösung würde so aussehen:

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

Update: Es ist sehr unwahrscheinlich, dass die Unterstützung von Eigenschaften in Java 7 oder vielleicht jemals hinzugefügt wird. Andere JVM-Sprachen wie Groovy, Scala usw. unterstützen diese Funktion jetzt. - Alex Miller


28
Das ist schade, ich mag Eigenschaften im C # -Stil (was so klingt, als würde man darüber reden)
Jon Onstott,

2
Verwenden Sie also Überladung ... private int _x; public void x (int value) {_x = value; } public int x () {return _x; }
Gordon

12
Ich bevorzuge die Verwendung =, was meiner Meinung nach den Code sauberer macht.
Svish

6
@ T-Bull: Nur weil du zwei haben kannst x, die zwei verschiedene Dinge sind, ist das keine gute Idee. IMHO, das ist ein schlechter Vorschlag, da er bei menschlichen Lesern zu Verwirrung führen könnte. Grundprinzip: Lassen Sie einen Leser nicht doppelt nehmen; Machen Sie deutlich, was Sie sagen - Verwenden Sie unterschiedliche Namen für unterschiedliche Entitäten. Auch wenn der Unterschied lediglich darin besteht, einen Unterstrich voranzustellen. Verlassen Sie sich nicht auf die umgebende Interpunktion, um die Entitäten zu unterscheiden.
ToolmakerSteve

2
@ToolmakerSteve: Das "könnte zu Verwirrung führen" ist das häufigste und immer noch schwächste Argument, wenn es um die Verteidigung des Codierungsdogmas geht (im Gegensatz zum Codierungsstil). Es gibt immer jemanden, der durch die einfachsten Dinge verwirrt werden könnte. Sie werden immer jemanden finden, der sich beschwert, dass er einen Fehler gemacht hat, nachdem er eine halbe Woche oder so wach geblieben ist und codiert hat und dann den irreführenden Codierungsstil beschuldigt hat. Ich lasse das nicht zählen. Dieser Stil ist gültig, offensichtlich und hält den Namespace sauber. Außerdem gibt es hier keine unterschiedlichen Entitäten, es gibt / one / entity und etwas Boilerplate-Code.
T-Bull

290

Es scheint, dass viele Java-Benutzer nicht mit den Sun Java-Codierungsrichtlinien vertraut sind, nach denen es durchaus angebracht ist, eine öffentliche Instanzvariable zu verwenden, wenn die Klasse im Wesentlichen eine "Struktur" ist, wenn Java "Struktur" unterstützt (wenn kein Verhalten vorliegt).

Die Leute neigen dazu zu denken, dass Getter und Setter der Java-Weg sind, als ob sie das Herz von Java sind. Das ist nicht so. Wenn Sie die Sun Java-Codierungsrichtlinien befolgen und in geeigneten Situationen öffentliche Instanzvariablen verwenden, schreiben Sie tatsächlich besseren Code, als ihn mit unnötigen Gettern und Setzern zu überladen.

Java Code Conventions von 1999 und noch unverändert.

10.1 Zugriff auf Instanz- und Klassenvariablen bereitstellen

Machen Sie keine Instanz- oder Klassenvariablen ohne guten Grund öffentlich. Oft müssen Instanzvariablen nicht explizit gesetzt oder abgerufen werden - was häufig als Nebeneffekt von Methodenaufrufen auftritt.

Ein Beispiel für geeignete öffentliche Instanzvariablen ist der Fall, in dem die Klasse im Wesentlichen eine Datenstruktur ohne Verhalten ist. Mit anderen Worten, wenn Sie eine Struktur anstelle einer Klasse verwendet hätten (wenn Java die Struktur unterstützt), sollten Sie die Instanzvariablen der Klasse öffentlich machen .

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


88
+1 Für eine maßgebliche Quelle. Jede andere Antwort ist, dass Menschen ihre Meinung so äußern, als wären sie Fakten.
ArtOfWarfare

1
Es gibt eine Java Beans-Spezifikation, die eine branchenübliche Methode für den Zugriff auf Eigenschaften mithilfe von get- und set-Methoden darstellt. Eine Übersicht finden Sie unter en.wikipedia.org/wiki/JavaBeans .
user924272

4
@ user924272: Wie ist diese Java Beans-Spezifikation für diese Antwort relevant, in der erläutert wird, wann die Verwendung von "öffentlichen Instanzvariablen" angemessen ist? Wenn die Spezifikation eine Standardmethode wäre, um Instanzvariablen automatisch in Eigenschaften umzuwandeln, z. B. C #, könnte sie relevant sein. Aber es ist nicht richtig? Es gibt lediglich die Benennung der Boilerplate-Getter und Setter an, die erstellt werden müssten, um eine solche Zuordnung vorzunehmen.
ToolmakerSteve

@ ToolmakerSteve. Dies ist eine Java-Frage. Die Frage entzieht sich auch einem häufigen Problem, bei dem eine Spezifikation existiert. Aus Sicht der alten Schule ist es viel einfacher, Feldmutationen zu debuggen, wenn es eine Standardmethode gibt - einen Haltepunkt bei einem Setter festlegen. Dies wurde wahrscheinlich mit modernen Debuggern überholt, aber ich bin gezwungen, Klassen zu missbilligen, die Objekte direkt "lochen" ... während dies für kleinere Anwendungen in Ordnung ist,
bereitet es

223

Verwenden Sie wirklich gesunden Menschenverstand. Wenn Sie etwas haben wie:

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

Dann macht es wenig Sinn, sie in Getter und Setter einzuwickeln. Sie werden niemals eine x, y-Koordinate in ganzen Pixeln auf andere Weise speichern. Getter und Setter werden Sie nur verlangsamen.

Auf der anderen Seite mit:

public class BankAccount{
    public int balance;
}

Möglicherweise möchten Sie die Art und Weise ändern, wie ein Saldo zu einem späteren Zeitpunkt berechnet wird. Dies sollte wirklich Getter und Setter verwenden.

Es ist immer besser zu wissen, warum Sie bewährte Verfahren anwenden, damit Sie wissen, wann es in Ordnung ist, die Regeln zu ändern.


3
Ich stimme dieser Antwort zu und sage weiter, dass Sie eine Klasse mit öffentlichen Feldern erstellen können, solange die Felder unabhängig voneinander fett gedruckt sind . dh ein Feld hängt nicht von einem anderen ab. Dies kann in vielen Fällen sehr nützlich sein, für mehrere Rückgabewerte einer Funktion oder für polare Co-Odinate. {Winkel, Länge}, die zusammenpassen, aber in keiner Weise voneinander abhängen.
Spacen Jasset

@SpacenJasset: Zu Ihrer Information, ich sehe nicht, wie Ihre Beispiele (mehrere Rückgabewerte; Polarkoordinaten) einen Einfluss darauf haben, ob öffentliche Felder gegen Getter / Setter verwendet werden sollen. Bei mehreren Rückgabewerten kann dies sogar kontraproduktiv sein, da der Aufrufer wohl nur die zurückgegebenen Werte abrufen sollte, was für öffentliche Getter und private Setter (unveränderlich) spricht. Dies kann auch für die Rückgabe von Polarkoordinaten von einem (x, y) -Objekt zutreffen. Berücksichtigen Sie akkumulierte mathematische Fehler, da Änderungen an einzelnen Komponenten der Polarkoordinate zurück in (x, y) konvertiert werden.
ToolmakerSteve

@SpacenJasset: Aber ich stimme Ihrem Prinzip zu.
ToolmakerSteve

1
Sie haben einen gültigen Punkt, aber ich denke, dass die Sache mit den Pixeln ein schlechtes Beispiel ist, da es etwas ist , das jemand tun könnte, um sicherzustellen, dass sich das Pixel innerhalb des Fensters befindet (nur ein Beispiel) , und außerdem zuzulassen, dass jemand das Pixel (-5, -5)möglicherweise nicht einstellt eine gute Idee. :-)
Horsey

50

Um Bedenken hinsichtlich der Veränderlichkeit auszuräumen, können Sie x und y als endgültig deklarieren. Beispielsweise:

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

Wenn Sie Code aufrufen, der versucht, in diese Felder zu schreiben, wird der Kompilierungszeitfehler "Feld x wird als endgültig deklariert; kann nicht zugewiesen werden" angezeigt.

Der Client-Code kann dann die in Ihrem Beitrag beschriebene "Kurzhand" -Komfort haben

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();
    }
}

3
Danke - eine Antwort, die nützlich und prägnant ist. Zeigt, wie Sie die Vorteile der Feldsyntax nutzen können, wenn Sie keine Veränderlichkeit wünschen.
ToolmakerSteve

@ToolmakerSteve - danke für das Feedback - sehr geschätzt.
Brian

Ich habe versucht, endgültige Instanzen einer endgültigen Klasse mit endgültigen Feldern als Struktur "Instanzen" zu verwenden, aber in einem caseAusdruck, der auf ein solches Instanzfeld verweist, habe ich erhalten Case expressions must be constant expressions. Wie geht das um? Was ist das idiomatische Konzept hier?
n611x007

1
Endfelder sind weiterhin Verweise auf Objekte, die keine Konstanten sind, da sie bei der ersten Verwendung der Klasse initialisiert werden. Der Compiler kann den "Wert" der Objektreferenzen nicht kennen. Konstanten müssen beim Kompilieren bekannt sein.
Johan Tidén

1
+1 Wirklich wichtige Antwort. Der Nutzen unveränderlicher Klassen ist meiner Meinung nach nicht zu unterschätzen. Ihre "Fire-and-Forget" -Semantik vereinfacht das Denken über Code häufig, insbesondere in Multithread-Umgebungen, in denen sie ohne Synchronisation willkürlich zwischen Threads geteilt werden können.
TheOperator

11

Verwenden Sie keine publicFelder

Verwenden Sie keine publicFelder, wenn Sie das interne Verhalten einer Klasse wirklich umbrechen möchten. Nehmen wir java.io.BufferedReaderzum Beispiel. Es hat das folgende Feld:

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

skipLFwird in allen Lesemethoden gelesen und geschrieben. Was ist, wenn eine externe Klasse, die in einem separaten Thread ausgeführt wird, den Status während eines Lesevorgangs in böswilliger Absicht geändert hat skipLF? BufferedReaderwird auf jeden Fall drunter und drüber gehen.

Verwenden Sie publicFelder

Nehmen Sie diese PointKlasse zum Beispiel:

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;
    }
}

Dies würde die Berechnung des Abstands zwischen zwei Punkten sehr schmerzhaft machen.

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));

Die Klasse hat kein anderes Verhalten als einfache Getter und Setter. Es ist akzeptabel, öffentliche Felder zu verwenden, wenn die Klasse nur eine Datenstruktur darstellt und kein Verhalten hat und niemals haben wird (Thin Getter und Setter werden hier nicht als Verhalten betrachtet). Es kann besser so geschrieben werden:

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));

Reinigen!

Aber denken Sie daran: Nicht nur Ihre Klasse muss kein Verhalten haben, sondern es sollte auch keinen Grund geben, sich auch in Zukunft zu verhalten.


(Genau dies wird in dieser Antwort beschrieben. So zitieren Sie "Code-Konventionen für die Java-Programmiersprache: 10. Programmierpraktiken" :

Ein Beispiel für geeignete öffentliche Instanzvariablen ist der Fall, in dem die Klasse im Wesentlichen eine Datenstruktur ohne Verhalten ist. Mit anderen Worten, wenn Sie eine structanstelle einer Klasse verwendet hätten (wenn Java unterstützt wird struct), ist es angebracht, die Instanzvariablen der Klasse öffentlich zu machen.

Die offizielle Dokumentation akzeptiert diese Praxis also auch.)


Wenn Sie besonders sicher sind, dass Mitglieder der oben genannten PointKlasse unveränderlich sein sollten, können Sie ein finalSchlüsselwort hinzufügen , um dies durchzusetzen:

public final double x;
public final double y;

8

Die Struktur, die Sie als Beispiel angeben, ist übrigens bereits in der Java-Basisklassenbibliothek als vorhanden java.awt.Point. Es hat x und y als öffentliche Felder, überzeugen Sie sich selbst .

Wenn Sie wissen, was Sie tun, und andere in Ihrem Team davon wissen, ist es in Ordnung, öffentliche Felder zu haben. Sie sollten sich jedoch nicht darauf verlassen, da sie Kopfschmerzen verursachen können, wie bei Fehlern, die mit Entwicklern zusammenhängen, die Objekte verwenden, als wären sie stapelzugeordnete Strukturen (Java-Objekte werden immer als Referenzen und nicht als Kopien an Methoden gesendet).


+1 Gute Erwähnung eines Problems - eine Art und Weise, in der das Ergebnis immer noch NICHT wie eine C-Struktur ist. Das Problem, das Sie bei Java-Objekten ansprechen, die immer als Referenz dienen, wird jedoch nicht dadurch verbessert, dass ein Setter erstellt wird, anstatt ein öffentlich beschreibbares Feld zu haben (was der Kern der Frage von OP ist - welche Darstellung verwendet werden soll). Es ist vielmehr ein Argument dafür, NIEMALS zu tun. Es ist ein Argument für IMMUTABILITY. Dies kann entweder als public finalFeld erfolgen, wie in Brians Antwort, oder durch einen öffentlichen Getter, aber keinen öffentlichen Setter. Das heißt, ob man Felder oder Accessoren verwendet, ist unerheblich.
ToolmakerSteve

8

Re: aku, izb, John Topley ...

Achten Sie auf Veränderlichkeitsprobleme ...

Es mag sinnvoll erscheinen, Getter / Setter wegzulassen. In einigen Fällen kann es tatsächlich in Ordnung sein. Das eigentliche Problem mit dem hier gezeigten vorgeschlagenen Muster ist die Veränderlichkeit.

Das Problem besteht darin, dass Sie eine Objektreferenz mit nicht endgültigen öffentlichen Feldern übergeben. Alles andere mit dieser Referenz kann diese Felder ändern. Sie haben keine Kontrolle mehr über den Status dieses Objekts. (Überlegen Sie, was passieren würde, wenn Strings veränderlich wären.)

Es wird schlecht, wenn dieses Objekt ein wichtiger Teil des internen Status eines anderen ist. Sie haben gerade die interne Implementierung verfügbar gemacht. Um dies zu verhindern, muss stattdessen eine Kopie des Objekts zurückgegeben werden. Dies funktioniert, kann jedoch einen massiven GC-Druck durch Tonnen von einmal erstellten Einwegkopien verursachen.

Wenn Sie öffentliche Felder haben, sollten Sie die Klasse schreibgeschützt machen. Fügen Sie dem Konstruktor die Felder als Parameter hinzu und markieren Sie die Felder als endgültig. Stellen Sie andernfalls sicher, dass Sie den internen Status nicht verfügbar machen. Wenn Sie neue Instanzen für einen Rückgabewert erstellen müssen, stellen Sie sicher, dass dieser nicht übermäßig aufgerufen wird.

Siehe: " Effective Java " von Joshua Bloch - Punkt 13: Unveränderlichkeit begünstigen.

PS: Denken Sie auch daran, dass alle JVMs heutzutage die getMethod nach Möglichkeit optimieren, was zu nur einer einzigen Feldleseanweisung führt.


12
Wie löst ein Getter / Setter dieses Problem? Sie haben noch eine Referenz, Sie haben keine Synchronisation mit den Operationen. Getter / Setter bieten an und für sich keinen Schutz.
he_the_great

1
Getter und Setter können bei Bedarf eine Synchronisation bereitstellen. Sie erwarten auch, dass Getter und Setter viel mehr tun, als sie angegeben wurden. Unabhängig davon bleibt das Problem der Synchronisation immer noch bestehen.
user924272

7

Ich habe dies in einigen Projekten versucht, mit der Theorie, dass Getter und Setter den Code mit semantisch bedeutungslosem Cruft überladen und dass andere Sprachen mit konventionellem Verstecken von Daten oder Aufteilen von Verantwortlichkeiten (z. B. Python) gut zurechtkommen.

Wie andere oben erwähnt haben, gibt es zwei Probleme, auf die Sie stoßen und die nicht wirklich behoben werden können:

  • Nahezu jedes automatisierte Tool in der Java-Welt basiert auf der Getter / Setter-Konvention. Das Gleiche gilt für, wie von anderen angemerkt, JSP-Tags, Federkonfiguration, Eclipse-Tools usw. usw. Der Kampf gegen das, was Ihre Tools erwarten, ist ein Rezept für lange Sitzungen, die durch Google trollen und versuchen, diese nicht standardmäßige Art der Initiierung zu finden Frühlingsbohnen. Wirklich nicht die Mühe wert.
  • Sobald Sie Ihre elegant codierte Anwendung mit Hunderten von öffentlichen Variablen haben, werden Sie wahrscheinlich mindestens eine Situation finden, in der sie nicht ausreichen - wo Sie unbedingt Unveränderlichkeit benötigen oder ein Ereignis auslösen müssen, wenn die Variable gesetzt wird oder Sie werfen möchten Eine Ausnahme bei einer Variablenänderung, da dadurch ein Objektstatus auf etwas Unangenehmes gesetzt wird. Sie haben dann die nicht beneidenswerte Wahl, Ihren Code mit einer speziellen Methode überall dort zu überladen, wo direkt auf die Variable verwiesen wird, und haben ein spezielles Zugriffsformular für 3 der 1000 Variablen in Ihrer Anwendung.

Und dies ist im besten Fall das Szenario, in einem eigenständigen privaten Projekt zu arbeiten. Sobald Sie das Ganze in eine öffentlich zugängliche Bibliothek exportieren, werden diese Probleme noch größer.

Java ist sehr ausführlich und dies ist eine verlockende Sache. Tu es nicht.


Hervorragende Diskussion über die Probleme, auf öffentlichen Feldern unterwegs zu sein. Ein definitiver Mangel an Java, der mich ärgert, wenn ich von C # zurück zu Java wechseln muss (was aus Javas Unannehmlichkeiten hier gelernt hat).
ToolmakerSteve

4

Wenn der Java-Weg der OO-Weg ist, dann verstößt das Erstellen einer Klasse mit öffentlichen Feldern gegen die Prinzipien des Versteckens von Informationen, die besagen, dass ein Objekt seinen eigenen internen Status verwalten sollte. (Da ich Sie also nicht nur mit Jargon ausspucke, besteht ein Vorteil des Versteckens von Informationen darin, dass die internen Funktionen einer Klasse hinter einer Schnittstelle verborgen sind. Angenommen, Sie wollten den Mechanismus ändern, mit dem Ihre Strukturklasse eines ihrer Felder gespeichert hat. Sie müssen wahrscheinlich zurückgehen und alle Klassen ändern, die die Klasse verwenden ...)

Sie können auch nicht die Unterstützung für JavaBean-Klassen nutzen, die mit der Benennung kompatibel sind. Dies kann schaden, wenn Sie beispielsweise die Klasse auf einer JavaServer-Seite verwenden, die mit Expression Language geschrieben wurde.

Der JavaWorld-Artikel Warum Getter- und Setter-Methoden böse sind, könnte Sie auch interessieren, wenn Sie darüber nachdenken, wann Sie keine Accessor- und Mutator-Methoden implementieren sollten.

Wenn Sie eine kleine Lösung schreiben und die Menge an Code minimieren möchten, ist der Java-Weg möglicherweise nicht der richtige - ich denke, es hängt immer von Ihnen und dem Problem ab, das Sie lösen möchten.


1
+1 für den Link zum Artikel "Warum Getter- und Setter-Methoden böse sind". Ihre Antwort wäre jedoch klarer gewesen, wenn Sie darauf hingewiesen hätten, dass sowohl öffentliche Felder als auch öffentliche Getter / Setter GLEICH NICHT Java-fähig sind: Wie in diesem Artikel erläutert - tun Sie dies nach Möglichkeit auch nicht. Stellen Sie den Clients stattdessen Methoden zur Verfügung, die für ihre Aufgaben spezifisch sind, anstatt die interne Darstellung der Instanz zu spiegeln. Dies betrifft jedoch nicht wirklich die gestellte Frage (die für einen einfachen "Struktur" -Fall verwendet werden soll), die von developer.g, izb und Brian besser beantwortet wird.
ToolmakerSteve

3

An dieser Art von Code ist nichts auszusetzen, vorausgesetzt, der Autor weiß, dass es sich um Strukturen (oder Daten-Shuttles) anstelle von Objekten handelt. Viele Java-Entwickler können den Unterschied zwischen einem wohlgeformten Objekt (nicht nur einer Unterklasse von java.lang.Object, sondern einem echten Objekt in einer bestimmten Domäne) und einer Ananas nicht erkennen. Ergo schreiben sie am Ende Strukturen, wenn sie Objekte benötigen und umgekehrt.


Die Ananas bringt mich zum Lachen :)
Guillaume

Diese Antwort sagt jedoch nichts darüber aus, was dieser Unterschied ist. Wenn Sie sich nicht mit ungelernten Entwicklern auseinandersetzen, wissen diese Entwickler nicht, wann sie eine Struktur erstellen und wann sie eine Klasse erstellen müssen.
ToolmakerSteve

Es sagt nichts über den Unterschied aus, weil der Autor sich des Unterschieds bewusst ist (er weiß, was eine strukturähnliche Entität ist). Der Autor fragt, ob die Verwendung von Strukturen in einer OO-Sprache angemessen ist, und ich sage ja (abhängig von der Problemdomäne). Wenn die Frage lautete, was ein Objekt zu einem echten Domänenobjekt macht, hätten Sie ein Bein, in dem Sie stehen können Streit.
Luis.espinal

Darüber hinaus ist der einzige Typ von ungelernten Entwicklern, der den Unterschied nicht kennen sollte, derjenige, der noch in der Schule ist. Dies ist äußerst grundlegend, beispielsweise um den Unterschied zwischen einer verknüpften Liste und einer Hash-Tabelle zu kennen. Wenn eine Person einen 4-jährigen Software-Abschluss hat, ohne zu wissen, was ein echtes Objekt ist, ist diese Person entweder nicht für diese Karriere geeignet, oder sie sollte wieder zur Schule gehen und eine Rückerstattung verlangen. Es ist mein ernst.
Luis.espinal

Aber um Ihre Beschwerde zu befriedigen, werde ich eine Antwort geben (die wenig mit der ursprünglichen Frage zu tun hat und einen eigenen Thread verdient). Objekte haben Verhalten und kapseln den Status. Strukturen nicht. Objekte sind Zustandsmaschinen. Strukturen dienen nur zum Aggregieren von Daten. Wenn die Leute eine ausführlichere Antwort wünschen, können sie eine neue Frage erstellen, auf die wir nach Herzenslust eingehen können.
Luis.espinal

2

Das Problem bei der Verwendung des öffentlichen Feldzugriffs ist das gleiche Problem wie bei der Verwendung von new anstelle einer Factory-Methode. Wenn Sie später Ihre Meinung ändern, sind alle vorhandenen Anrufer fehlerhaft. Aus Sicht der API-Evolution ist es daher normalerweise eine gute Idee, in die Kugel zu beißen und Getter / Setter zu verwenden.

Ein Ort, an dem ich den anderen Weg gehe, ist, wenn Sie den Zugriff auf die Klasse stark kontrollieren, beispielsweise in einer inneren statischen Klasse, die als interne Datenstruktur verwendet wird. In diesem Fall ist es möglicherweise viel klarer, den Feldzugriff zu verwenden.

Übrigens ist es nach Aussage von e-bartek sehr unwahrscheinlich, dass IMO die Unterstützung von Eigenschaften in Java 7 hinzugefügt wird.


1
Dies ist wahr, aber wenn Sie Java verwenden, ist dies kein Problem, da Sie eine gesamte Codebasis mit einem Klick umgestalten können (Eclipse, Netbeans, wahrscheinlich auch VIM und Emacs). Wenn Sie sich für einen seiner dynamischen Freunde wie Groovy entschieden haben, können Sie selbst mit einer einfachen Kapselung stunden- oder tagelang Probleme beheben. Zum Glück hätten Sie Ihre Testfälle, die Sie darüber informieren würden, wie viel mehr Code Sie reparieren müssen.
Dan Rosenstark

2
Sie gehen natürlich davon aus, dass sich alle Benutzer in Ihrer Codebasis befinden, aber das stimmt natürlich oft nicht. Oft ist es aus verschiedenen nichttechnischen Gründen nicht einmal möglich, Code zu ändern, der sich möglicherweise in Ihrer eigenen Codebasis befindet.
Alex Miller

2

Ich verwende dieses Muster häufig beim Erstellen privater innerer Klassen, um meinen Code zu vereinfachen, würde jedoch nicht empfehlen, solche Objekte in einer öffentlichen API verfügbar zu machen. Im Allgemeinen ist es umso besser, je häufiger Sie Objekte in Ihrer öffentlichen API unveränderlich machen können, und es ist nicht möglich, Ihr 'strukturähnliches' Objekt unveränderlich zu erstellen.

Abgesehen davon würde ich, selbst wenn ich dieses Objekt als private innere Klasse schreiben würde, immer noch einen Konstruktor bereitstellen, um den Code zum Initialisieren des Objekts zu vereinfachen. Es ist einfach chaotisch, 3 Codezeilen zu haben, um ein verwendbares Objekt zu erhalten, wenn man es tut.


2

Eine sehr sehr alte Frage, aber lassen Sie mich noch einen kurzen Beitrag leisten. Java 8 führte Lambda-Ausdrücke und Methodenreferenzen ein. Lambda-Ausdrücke können einfache Methodenreferenzen sein und keinen "wahren" Körper deklarieren. Sie können ein Feld jedoch nicht in eine Methodenreferenz "konvertieren". So

stream.mapToInt(SomeData1::x)

ist nicht legal, aber

stream.mapToInt(SomeData2::getX)

ist.


In diesem Fall können Sie verwenden data -> data.x, was immer noch vernünftig ist
James Kraus

1

Ich sehe den Schaden nicht, wenn Sie wissen, dass es immer eine einfache Struktur sein wird und dass Sie niemals Verhalten daran anhängen wollen.


1

Dies ist eine Frage zum objektorientierten Design, nicht zur Sprache Java. Im Allgemeinen empfiehlt es sich, Datentypen innerhalb der Klasse auszublenden und nur die Methoden bereitzustellen, die Teil der Klassen-API sind. Wenn Sie interne Datentypen verfügbar machen, können Sie diese in Zukunft nie mehr ändern. Wenn Sie sie ausblenden, besteht Ihre einzige Verpflichtung gegenüber dem Benutzer in den Rückgabe- und Argumenttypen der Methode.


1

Hier erstelle ich ein Programm zur Eingabe von Name und Alter von 5 verschiedenen Personen und führe eine Auswahlsortierung durch (altersmäßig). Ich habe eine Klasse verwendet, die als Struktur fungiert (wie die Programmiersprache C), und eine Hauptklasse, um die gesamte Operation auszuführen. Hierunter richte ich den Code ein ...

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+")");
    }
}

Der obige Code ist fehlerfrei und getestet ... Kopieren Sie ihn einfach und fügen Sie ihn in Ihre IDE ein und ... Sie wissen und was ??? :) :)


0

Sie können eine einfache Klasse mit öffentlichen Feldern und ohne Methoden in Java erstellen, aber es ist immer noch eine Klasse und wird immer noch syntaktisch und in Bezug auf die Speicherzuweisung wie eine Klasse behandelt. Es gibt keine Möglichkeit, Strukturen in Java wirklich zu reproduzieren.


0

Manchmal verwende ich eine solche Klasse, wenn ich mehrere Werte von einer Methode zurückgeben muss. Natürlich ist ein solches Objekt von kurzer Dauer und mit sehr eingeschränkter Sichtbarkeit, daher sollte es in Ordnung sein.


0

Wie bei den meisten Dingen gibt es die allgemeine Regel und dann gibt es bestimmte Umstände. Wenn Sie eine geschlossene, erfasste Anwendung ausführen, damit Sie wissen, wie ein bestimmtes Objekt verwendet wird, können Sie mehr Freiheit ausüben, um Sichtbarkeit und / oder Effizienz zu fördern. Wenn Sie eine Klasse entwickeln, die von anderen außerhalb Ihrer Kontrolle öffentlich verwendet wird, lehnen Sie sich an das Getter / Setter-Modell. Verwenden Sie wie bei allen Dingen nur den gesunden Menschenverstand. Es ist oft in Ordnung, eine erste Runde mit der Öffentlichkeit zu machen und sie später in Getter / Setter zu ändern.


0

Mit der aspektorientierten Programmierung können Sie Zuweisungen oder Abrufe abfangen und ihnen eine Abfanglogik hinzufügen. Ich schlage vor, dies ist der richtige Weg, um das Problem zu lösen. (Die Frage, ob sie öffentlich oder geschützt oder paketgeschützt sein sollen, ist orthogonal.)

Sie beginnen also mit nicht abgefangenen Feldern mit dem richtigen Zugriffsqualifizierer. Wenn Ihre Programmanforderungen wachsen, fügen Sie eine Logik hinzu, um möglicherweise zu validieren, eine Kopie des zurückgegebenen Objekts zu erstellen usw.

Die Getter / Setter-Philosophie verursacht Kosten für eine große Anzahl einfacher Fälle, in denen sie nicht benötigt werden.

Ob der Aspektstil sauberer ist oder nicht, ist etwas qualitativ. Ich würde es einfach finden, nur die Variablen in einer Klasse zu sehen und die Logik separat zu betrachten. Tatsächlich ist das Grundprinzip für eine apect-orientierte Programmierung, dass viele Probleme sich überschneiden und eine Unterteilung in den Klassenkörper selbst nicht ideal ist (Protokollierung ist ein Beispiel - wenn Sie alles protokollieren möchten, was Java von Ihnen verlangt Schreiben Sie eine ganze Reihe von Gettern und halten Sie sie synchron, aber AspectJ ermöglicht Ihnen einen Einzeiler.

Das Problem der IDE ist ein roter Hering. Es ist weniger das Tippen als vielmehr das Lesen und die visuelle Verschmutzung, die durch get / sets entstehen.

Anmerkungen scheinen auf den ersten Blick der aspektorientierten Programmierung ähnlich zu sein, erfordern jedoch eine ausführliche Aufzählung von Punktschnitten durch Anhängen von Anmerkungen, im Gegensatz zu einer prägnanten Wildcard-ähnlichen Punktschnitt-Spezifikation in AspectJ.

Ich hoffe, dass das Bewusstsein für AspectJ verhindert, dass sich Menschen vorzeitig für dynamische Sprachen entscheiden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.