Ist der Umfang auf Java-Paketebene nützlich?


11

Ich verstehe die Idee des Paketumfangs und habe manchmal sogar gedacht, dass ich es wollte. Jedes Mal, wenn ich mich mit der ernsthaften Absicht auf den Weg machte, es zu versuchen, stellte ich fest, dass es nicht den Anforderungen entsprach, von denen ich dachte, dass es dienen würde.

Mein Hauptproblem scheint immer zu sein, dass die Dinge, deren Umfang ich einschränken möchte, niemals im selben Paket enthalten sind. Sie können konzeptionell alle miteinander verknüpft sein, aber die logische Aufteilung der Daten innerhalb der Anwendung hat sie als separate untergeordnete Pakete eines größeren Pakets.

Zum Beispiel kann ich ein Missionsmodell haben und ich möchte, dass nur andere Missionswerkzeuge, wie meine missionServices, einige Methoden verwenden. Am Ende habe ich jedoch Missions.models und Missions.services als meine Pakete, sodass MissionModel und MissionService nicht denselben Paketumfang haben. Es scheint nie eine Situation zu geben, in der Pakete die Dinge angemessen enthalten, für die ich erhöhte Berechtigungen haben möchte, ohne auch zahlreiche Dinge zu enthalten, für die ich diese Berechtigungen nicht haben möchte. und selten habe ich das Gefühl, dass der Vorteil des Paketumfangs eine Methode rechtfertigt, die meine Projektarchitektur so ändert, dass alles im selben Paket platziert wird. Oft erweisen sich entweder Aspekte oder eine Art Umkehrung der Kontrolle als der bessere Ansatz für jedes Problem, für das ich kurz über das Scoping von Paketen nachgedacht habe.

Ich bin eher neugierig, dass dies allgemein von allen Java-Entwicklern als wahr angesehen wird oder nur ein Zufall meiner Arbeit ist. Wird der Paketumfang in der realen Welt häufig genutzt? Gibt es viele Fälle, in denen die Verwendung als gute Form angesehen wird, oder wird sie meist als altes Verhalten angesehen, das in der modernen Entwicklung selten ausgenutzt wird?

Ich frage nichts darüber, warum der private Bereich des Pakets standardmäßig ist. Ich frage, wann es unabhängig von den Standardeinstellungen verwendet werden soll. Die meisten Diskussionen darüber, warum es sich um eine Standardeinstellung handelt, werden nicht wirklich behandelt, wenn der Paketumfang tatsächlich nützlich ist. Stattdessen wird lediglich argumentiert, warum die beiden anderen häufig verwendeten Bereiche nicht standardmäßig sein sollten, damit das Paket durch Eliminierung gewinnt. Außerdem geht es bei meiner Frage um den aktuellen Entwicklungsstand. Insbesondere haben wir uns so weit entwickelt, dass andere Tools und Paradigmen den Umfang des Pakets weniger nützlich machen als damals, als die Entscheidung, es als Standard festzulegen, Sinn machte.


Gedankenexperiment: Wie würden Java-Programme aussehen, wenn Sie keine Pakete hätten? Es kann sein, dass Sie sich keine großen Java-Programme angesehen oder daran gearbeitet haben .
Robert Harvey

Schauen Sie sich den Code für java.util.Stringund an java.util.Integer.


2
Die am häufigsten gewählte Antwort auf die doppelte Frage deckt die wichtigste Verwendung für paketprivat ab und beantwortet implizit, warum sie nützlich ist.

2
Das Duplikat überzeugt mich überhaupt nicht. Diese Frage lautet: "Wofür ist der Paketumfang?" während der andere (unter anderem) fragt: "Der angegebene Paketumfang ist die Standardeinstellung, wofür ist der private Bereich?" Plus die akzeptierte Antwort auf die Betrogene und die akzeptierte Antwort hier machen ganz andere Punkte.
Ixrec

Antworten:


2

Im gesamten java.util.*Paket gibt es Fälle, in denen Code mit Schutz auf Paketebene geschrieben wird. Zum Beispiel dieses Bit java.util.String- ein Konstruktor in Java 6 :

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

oder diese getChars-Methode :

/** Copy characters from this string into dst starting at dstBegin. 
    This method doesn't perform any range checking. */
void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, offset, dst, dstBegin, count);
}

Der Grund dafür ist, dass die Entwickler des Codes (und java.util.*als eine ziemlich große Bibliothek betrachtet werden können) die Möglichkeit einer schnelleren Leistung auf Kosten eines Verlusts verschiedener Sicherheit (Bereichsüberprüfungen von Arrays, direkter Zugriff auf andere Felder) wünschten Dies impliziert eher die Implementierung als die Schnittstelle ("unsicherer" Zugriff auf Teile der Klasse, die ansonsten über öffentliche Methoden als unveränderlich angesehen werden).

Durch die Einschränkung, dass auf diese Methoden und Felder nur andere Klassen im java.utilPaket zugreifen können, wird eine engere Kopplung zwischen ihnen hergestellt, aber auch vermieden, dass diese Implementierungsdetails der Welt zugänglich gemacht werden .

Wenn Sie an einer Anwendung arbeiten und sich nicht um andere Benutzer kümmern müssen, müssen Sie sich keine Sorgen um den Schutz auf Paketebene machen. Abgesehen von verschiedenen Aspekten der Designreinheit könnten Sie alles machen publicund damit einverstanden sein.

Wenn Sie jedoch an einer Bibliothek arbeiten, die von anderen verwendet werden soll (oder vermeiden möchten, dass Ihre Klassen für ein späteres Refactoring zu stark verwickelt werden), sollten Sie das strengste Schutzniveau verwenden, das Ihnen für Ihre Anforderungen gewährt wird. Oft bedeutet dies private. Manchmal müssen Sie jedoch ein wenig der Implementierungsdetails anderen Klassen im selben Paket zur Verfügung stellen, um Wiederholungen zu vermeiden oder die eigentliche Implementierung auf Kosten der Kopplung der beiden Klassen und ihrer Implementierungen etwas zu vereinfachen. Somit ist der Schutz auf Paketebene.


1
Ich habe java.util durchgesehen und das sehe ich. Ich stelle jedoch auch fest, dass es sich um eine GROSSE Bibliothek handelt. Heutzutage scheinen so große Bibliotheken selten ohne Unterpakete geschrieben zu werden. und es sind diese Teilpakete, die zu Grenzen führen. Ich sage nicht, dass java.util falsch ist, aber es scheint, als könnten sie mit einem großen Paket davonkommen, nur weil sie EXTREM gute Programmierer hatten, denen sie vertrauen konnten, dass sie alles richtig machen, mit einer eher begrenzten Kapselung innerhalb eines Pakets. Ich würde mich nackt fühlen, wenn ich darauf vertraue, dass durchschnittliche Entwickler, die an ähnlichen Paketen wie ich arbeiten, so viel Potenzial haben, schlecht zu machen.
dsollen

1
@dsollen Wenn Sie sich mit Spring und Hibernate oder ähnlich großen Bibliotheken beschäftigen, werden Sie ähnliche Dinge finden. Es gibt Zeiten, in denen Sie eine engere Kopplung benötigen. Man sollte die Möglichkeit haben, die Einsendungen zu überprüfen und sicherzustellen, dass alles korrekt ist. Wenn Sie keine engere Kopplung benötigen oder den anderen Codierern überhaupt nicht vertrauen, sind öffentliche Felder, Pakete oder geschützte Felder oder Methoden nicht immer geeignet. Dies bedeutet jedoch nicht, dass es nicht nützlich ist.

Kurz gesagt, ich wäre versucht, eingeschränktere Pakete zu erstellen und mir keine Sorgen über den Optimierungsgrad (der in 95% der Projekte nur eine Verschwendung ist) für meine täglichen Bedürfnisse zu machen. Meine folgende Frage lautet daher: Ist der Umfang auf Paketebene ein sehr spezifisches Optimierungspotenzial, das nur in bestimmten häufig verwendeten massiven APIs verwendet wird? IE nur die Großen müssen oder wollen den Schutz in diesem Maße lockern? Gibt es Gründe, warum gute Programmierer, die an einer Standard-API mit kleineren Benutzerbasen arbeiten, dafür Verwendung finden würden?
dsollen

Ich habe meinen Follow-up-Beitrag nicht schnell genug erhalten :) Ich verstehe und sehe den Punkt, den Sie gemacht haben, ich diskutiere überhaupt nicht darüber. Es wird nur eine Folgefrage gestellt, ob es für den Rest von uns guten Programmierern nützlich ist, aber diejenigen, die nicht an so großen APIs arbeiten können. Insbesondere handelt es sich meistens um eine Optimierung auf Kosten des Kapselungskompromisses, die nur durchgeführt wird, wenn Sie die Leistung wirklich wirklich optimieren müssen.
dsollen

2
@dsollen geht es nicht um Leistung (obwohl dies ein Grund ist, die Implementierung aufzudecken), sondern um Kapselung. Sie tun dies, um zu vermeiden, dass der Code wiederholt wird, Integer.toString()und String.valueOf(int)können Code freigeben, ohne ihn der Welt auszusetzen. Die java.utilKlassen sollen zusammenarbeiten, um eine größere Gesamtheit von Funktionen bereitzustellen, anstatt einzelne Klassen zu sein, die vollständig Inseln für sich sind - sie sind zusammen in einem Paket und teilen die Implementierungsfunktionen über das Scoping auf Paketebene.

5

Manchmal eskaliere ich die Sichtbarkeit privater oder geschützter Methoden zum Paket, damit ein Junit-Test auf einige Implementierungsdetails zugreifen kann, auf die von außen nicht zugegriffen werden sollte.

Da der Junit-Test dasselbe Paket wie der zu testende Artikel enthält, kann er auf diese Implementierungsdetails zugreifen

Beispiel

  public class MyClass {
    public MyClass() {
        this(new MyDependency());
    }

    /* package level to allow junit-tests to insert mock/fake implementations */ 
    MyClass(IDependency someDepenency) {...}
  }

Es ist fraglich, ob dies eine "gute" Verwendung ist oder nicht, aber es ist pragmatisch


2
Ich habe Tests immer in ein anderes Paket gestellt, ansonsten habe ich eine Schlüsselmethode im Standardbereich belassen, die es niemand anderem erlaubt, sie aufzurufen ...
soru

Ich mache es nie mit Methoden, aber es ist eine nette Funktion für injizierte Abhängigkeiten. Wenn Sie beispielsweise EJBs testen, können injizierte EntityManager verspottet werden, indem Sie eine EM auf Paketebene mit einem Mock-Objekt festlegen, das zur Laufzeit vom Anwendungsserver injiziert wird.
Jwenting

Warum protectedzum Paketumfang aufsteigen? liefert protected bereits Paketzugriff. Es ist ein bisschen seltsam, aber ich denke, sie wollten keine zusammengesetzten Bereichsdeklarationen wie protected packagescope void doSomething()(für die auch ein neues Schlüsselwort erforderlich ist) benötigen.
tgm1024 - Monica wurde

2

Es ist nicht besonders nützlich, insbesondere als Standard, in einer Welt, in der Menschen dazu neigen, die gepunkteten Namen von Paketen so zu verwenden, als wären sie Unterpakete. Da Java kein Unterpaket hat, hat xyinternals nicht mehr Zugriff auf xy als auf ab. Paketnamen sind entweder gleich oder unterschiedlich. Eine teilweise Übereinstimmung unterscheidet sich nicht davon, nichts gemeinsam zu haben.

Ich denke, die ursprünglichen Entwickler hätten nie erwartet, dass diese Konvention verwendet wird, und wollten die Dinge einfach halten, ohne die Komplexität hierarchischer Pakete im Ada-Stil zu erhöhen.

Java 9 behebt dies, indem Sie mehrere Pakete in ein Modul einfügen und nur einige exportieren können. Dadurch wird der Paketumfang jedoch noch redundanter.

In einer idealen Welt würden sie den Standardbereich in "Modulbereich, wenn ein enthaltendes Modul vorhanden ist, andernfalls Paketbereich" ändern. Anschließend können Sie die Implementierung innerhalb eines Moduls freigeben und so das Refactoring ermöglichen, ohne die exportierten Schnittstellen zu beschädigen. Aber vielleicht gibt es eine gewisse Subtilität, warum dies die Abwärtskompatibilität beeinträchtigen oder auf andere Weise schlecht sein würde.


Ich finde den Java 9-Kommentar interessant. Ich denke, eine einfache Möglichkeit, Paketherachien zu erkennen und den Paketumfang in Bezug auf ein übergeordnetes Paket (mithilfe von Anmerkungen?) Zu definieren, wäre hilfreich.
dsollen

1
@dsollen vielleicht, aber meine erste Neigung ist, dass wir anfangen würden, die Reifeneisen damit zu vergolden. Ein weitaus besserer erster Schritt zur Klarheit (der in gewissem Maße Sicherheit mit sich bringt) wäre meiner Meinung nach die Erfindung eines tatsächlichen Schlüsselworts für den Paketumfang. Es könnte sogar "Paket" sein :)
tgm1024 - Monica wurde

2

Die Art der Kohäsion, die Sie beschreiben, ist nicht wirklich das beste Beispiel dafür, wo Sie den Paketzugriff verwenden würden. Das Paket ist praktisch zum Erstellen von Komponenten, Modulen, die auf einer Schnittstelle austauschbar sind.

Hier ist ein grobes Beispiel eines Szenarios. Angenommen, Ihr Code wurde neu organisiert, um den funktionalen Zusammenhalt und die Komponentisierung zu verbessern. Vielleicht 3 Gläser wie:

**MissionManager.jar** - an application which uses the Java Service Provider Interface to load some implementation of missions, possible provided by a 3rd party vendor.

**missions-api.jar** - All interfaces, for services and model
    com...missions
        MissionBuilder.java   - has a createMission method
        Mission.java - interface for a mission domain object
    com...missions.strategy
        ... interfaces that attach strategies to missions ...
    com...missions.reports
        .... interfaces todo with mission reports ....

   **MCo-missionizer-5000.jar** -  provides MCo's Missionizer 5000 brand mission implementation.
       com...missions.mco
                  Mission5000.java - implements the Mission interface.
       com...missions.strategy.mco
              ... strategy stuff etc ...

Durch die Verwendung der Paketebene in Mission5000.java können Sie sicher sein, dass kein anderes Paket oder keine andere Komponente wie der MissionManager eng mit der Implementierung der Mission verbunden werden kann. Dies ist nur die von "M co" bereitgestellte Komponente von "M co", die tatsächlich eine eigene spezielle Markenimplementierung einer Mission erstellt. Der Mission Manager muss MissionBuiler.createMission (...) aufrufen und die definierte Schnittstelle verwenden.

Auf diese Weise wissen wir, dass die Implementierer von MissionManager.jar die API nicht umgangen haben und die Implementierung von MCo direkt verwenden, wenn die Mission-Implementierungskomponente ersetzt wird.

Daher können wir MCo-missionizer5000.jar gegen ein Konkurrenzprodukt austauschen. (Oder vielleicht ein Mock zum Testen des Missionsmanagers)

Umgekehrt kann MCo einiges tun, um die Geschäftsgeheimnisse seiner eigenen Missionare zu verbergen.

(Dies hängt damit zusammen, dass Instabilitäts- und Abstraktheitsideen auch in Komponenten durchgesetzt werden.)


-1

Da der Paketumfang in Java nicht hierarchisch gestaltet ist, ist er kaum nützlich. Wir verwenden den Paketbereich überhaupt nicht und definieren alle Klassen als öffentlich.

Stattdessen verwenden wir unsere eigenen Zugriffsregeln, die auf der Pakethierarchie und den vordefinierten Paketnamen basieren.

Wenn Sie an Details interessiert sind, suchen Sie bei Google nach "CODERU-Regeln".

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.