Warum hat Java den Paketzugriff als Standard festgelegt?

26

Ich stelle diese Frage, weil ich glaube, dass sie es aus einem sehr guten Grund getan haben und dass die meisten Leute sie nicht richtig anwenden, auch aus meiner bisherigen Erfahrung in der Industrie. Aber wenn meine Theorie zutrifft, bin ich mir nicht sicher, warum sie den Modifikator für den privaten Zugriff enthielten ...?

Ich glaube, dass bei ordnungsgemäßer Verwendung des Standardzugriffs die Testbarkeit verbessert wird, während die Kapselung beibehalten wird. Außerdem wird der Modifikator für den privaten Zugriff überflüssig.

Der Standardzugriffsmodifikator kann verwendet werden, um denselben Effekt zu erzielen, indem ein eindeutiges Paket für Methoden verwendet wird, die vor dem Rest der Welt verborgen werden müssen, und dies, ohne die Testbarkeit zu beeinträchtigen, wie dies bei Paketen in einem Testordner der Fall ist Zugriff auf alle in einem Quellordner deklarierten Standardmethoden.

Ich glaube, aus diesem Grund verwendet Java den Paketzugriff als "Standard". Aber ich bin mir nicht sicher, warum sie auch privaten Zugriff enthielten, ich bin sicher, dass es einen gültigen Anwendungsfall gibt ...

newlogic
quelle
Relevant in Bezug auf zuvor diskutierte Unit-Tests privater Methoden; How-Do-You-Unit-Test-Private-Methoden . Antworten; Sie sollten nicht
Richard Tingle

Antworten:

25

Ich denke, sie hatten eine gute Vorstellung davon, was ein durchschnittlicher Programmierer tun würde. Und mit durchschnittlichem Programmierer meine ich einen, der nicht wirklich gut programmiert, aber trotzdem den Job bekommt, weil es nicht genug gute gibt und sie teuer sind.

Wenn sie den Standardzugriff "öffentlich" machen würden, würden sich die meisten Programmierer niemals die Mühe machen, etwas anderes zu verwenden. Das Ergebnis wäre überall eine Menge Spaghetti-Code . (Denn wenn Sie technisch irgendwo etwas aufrufen dürfen, warum sollten Sie sich dann die Mühe machen, eine Logik in einer Methode zu kapseln?)

Wenn Sie "privat" als Standardzugriff festlegen, ist dies nur geringfügig besser. Die meisten anfänglichen Programmierer erfinden einfach eine Faustregel (und teilen sie auf ihren Blogs), dass "man überall" öffentlich schreiben muss, und beschweren sich dann darüber, warum Java so schlecht ist, dass sie gezwungen werden, überall "öffentlich" zu schreiben. Und sie würden auch viel Spaghetti-Code produzieren.

Der Paketzugriff ermöglicht es Programmierern, ihre schlampigen Programmiertechniken beim Schreiben von Code innerhalb eines Pakets zu verwenden, muss jedoch beim Erstellen weiterer Pakete neu überdacht werden. Es ist ein Kompromiss zwischen guten Geschäftspraktiken und der hässlichen Realität. Wie zum Beispiel: Schreibe einen Spaghetti-Code, wenn du darauf bestehst, aber lass das hässliche Durcheinander bitte im Paket; Erstellen Sie zumindest einige schönere Schnittstellen zwischen den Paketen.

Wahrscheinlich gibt es auch andere Gründe, aber ich würde diesen nicht unterschätzen.

Viliam Búr
quelle
14

Der Standardzugriff macht den Modifikator für den privaten Zugriff nicht überflüssig.

Die Position der Sprachdesigner dazu spiegelt sich im offiziellen Tutorial - Kontrolle des Zugangs zu Mitgliedern einer Klasse - wider und ist ziemlich klar (für Ihre Bequemlichkeit relevante Aussage in dem fett gedruckten Zitat ):

Tipps zur Auswahl einer Zugriffsebene:

Wenn andere Programmierer Ihre Klasse verwenden, möchten Sie sicherstellen, dass keine Fehler aufgrund von Missbrauch auftreten können. Zugriffsebenen können Ihnen dabei helfen.

  • Verwenden Sie die restriktivste Zugriffsebene, die für ein bestimmtes Mitglied sinnvoll ist. Verwenden Sie privat, es sei denn, Sie haben einen guten Grund, dies nicht zu tun.
  • Vermeiden Sie öffentliche Felder mit Ausnahme von Konstanten. (In vielen Beispielen des Lernprogramms werden öffentliche Felder verwendet. Dies kann zur Veranschaulichung einiger Punkte hilfreich sein, wird jedoch für Produktionscode nicht empfohlen.) Öffentliche Felder verlinken Sie in der Regel mit einer bestimmten Implementierung und schränken Ihre Flexibilität beim Ändern Ihres Codes ein.

Ihr Appell an die Testbarkeit als Rechtfertigung für den vollständigen Wegfall des Modifikators "Private" ist falsch, wie z. B. die Antworten in " Neu bei TDD" belegen . Sollte ich jetzt private Methoden meiden?

Natürlich können Sie private Methoden haben und natürlich können Sie sie testen.

Entweder gibt es eine Möglichkeit, die private Methode zum Laufen zu bringen. In diesem Fall können Sie sie auf diese Weise testen, oder es gibt keine Möglichkeit , die private Methode zum Laufen zu bringen. In diesem Fall: Warum zum Teufel versuchen Sie es nur zu testen? lösche das verdammte Ding ...


Die Position der Sprachentwickler in Bezug auf den Zweck und die Verwendung des Zugriffs auf Paketebene wird in einem anderen offiziellen Tutorial, Erstellen und Verwenden von Paketen, erläutert. Es hat nichts mit der Idee zu tun, private Modifikatoren zu löschen (für Ihre Bequemlichkeit relevante Aussage im fett gedruckten Zitat ). :

Sie sollten diese Klassen und die Schnittstelle aus verschiedenen Gründen in einem Paket bündeln, unter anderem aus folgenden Gründen:

  • Sie und andere Programmierer können leicht feststellen, dass diese Typen verwandt sind ...
  • Sie können zulassen, dass Typen innerhalb des Pakets uneingeschränkten Zugriff aufeinander haben und dennoch den Zugriff für Typen außerhalb des Pakets einschränken ...

<rant> Ich glaube, ich habe genug Gejammer gehört. Schätze, es ist an der Zeit, laut und deutlich zu sagen ... </ rant>

Private Methoden sind für Unit-Tests von Vorteil.

Der folgende Hinweis setzt voraus, dass Sie mit der Codeabdeckung vertraut sind . Wenn nicht, nehmen Sie sich Zeit zum Lernen, da dies für diejenigen, die an Unit-Tests interessiert sind und überhaupt testen möchten, sehr nützlich ist.

Also gut, ich habe diese privaten Methoden- und Unit-Tests und die Coverage-Analyse, die mir sagt, dass es eine Lücke gibt, meine private Methode wird nicht durch Tests abgedeckt. Jetzt...

Was verdiene ich, wenn ich es privat halte?

Da die Methode privat ist, müssen Sie nur den Code untersuchen, um zu erfahren, wie er über die nicht-private API verwendet wird. Typischerweise zeigt eine solche Studie, dass der Grund für die Lücke darin liegt, dass in Tests ein bestimmtes Nutzungsszenario fehlt.

    void nonPrivateMethod(boolean condition) {
        if (condition) {
            privateMethod();
        }
        // other code...
    }

    // unit tests don't invoke nonPrivateMethod(true)
    //   => privateMethod isn't covered.

Der Vollständigkeit halber könnten andere (weniger häufige) Gründe für solche Abdeckungslücken Fehler in der Spezifikation / Konstruktion sein. Ich werde hier nicht weiter darauf eingehen, um die Dinge einfach zu halten. Es reicht zu sagen, dass Sie die Chance verpassen werden, zu erfahren, dass diese Fehler überhaupt existieren, wenn Sie die Zugriffsbeschränkung "nur um die Methode testbar zu machen" schwächen.

Gut, um die Lücke zu schließen, füge ich einen Komponententest für das fehlende Szenario hinzu, wiederhole die Abdeckungsanalyse und stelle sicher, dass die Lücke weg ist. Was habe ich jetzt? Ich habe als Neuheit einen Unit-Test für die spezifische Nutzung der nicht-privaten API.

  1. Ein neuer Test stellt sicher, dass sich das erwartete Verhalten für diese Verwendung nicht ohne Vorankündigung ändert, da der Test fehlschlägt, wenn er sich ändert.

  2. Ein externer Leser kann sich diesen Test ansehen und lernen, wie er verwendet werden soll und wie er sich verhält (hier schließt der externe Leser mein zukünftiges Ich ein, da ich den Code ein oder zwei Monate, nachdem ich damit fertig bin, vergesse).

  3. Neuer Test ist tolerant gegenüber Refactoring (refactor ich private Methoden? Wetten Sie!) Was auch immer ich tue privateMethod, ich werde immer testen wollen nonPrivateMethod(true). Unabhängig davon, was ich tue privateMethod, muss der Test nicht geändert werden, da die Methode nicht direkt aufgerufen wird.

Nicht schlecht? Wetten Sie?

Was verliere ich durch die Schwächung der Zugriffsbeschränkung?

Stellen Sie sich nun vor, dass ich statt der oben genannten lediglich die Zugriffsbeschränkung abschwäche. Ich überspringe das Studium des Codes, der die Methode verwendet, und fahre direkt mit dem Test fort, der my aufruft exPrivateMethod. Groß? Nicht!

  1. Erhalte ich einen Test für die spezifische Nutzung der oben genannten nicht-privaten API? Nein, es gab nonPrivateMethod(true)vorher keinen Test , und jetzt gibt es keinen solchen Test.

  2. Erhalten externe Leser die Möglichkeit, die Verwendung der Klasse besser zu verstehen? Nein. "- Hey, was ist der Zweck der hier getesteten Methode? - Vergiss es, sie ist ausschließlich für den internen Gebrauch bestimmt. - Ups."

  3. Ist es tolerant für Refactoring? Auf keinen Fall: Was auch immer ich ändere exPrivateMethod, wird den Test wahrscheinlich brechen. Benennen Sie um, führen Sie sie in eine andere Methode ein, ändern Sie die Argumente, und der Test hört einfach auf zu kompilieren. Kopfschmerzen? Wetten Sie?

Zusammenfassend kann ich sagen , dass das Festhalten an der privaten Methode eine nützliche und zuverlässige Verbesserung bei Unit-Tests darstellt. Im Gegensatz dazu gibt mir die Schwächung der Zugriffsbeschränkungen "für die Testbarkeit" nur einen undurchsichtigen, schwer verständlichen Teil des Testcodes, der außerdem permanent durch geringfügiges Refactoring beschädigt werden kann. ehrlich gesagt, was ich bekomme, sieht verdächtig nach technischer Verschuldung aus .

</ rant>

Mücke
quelle
7
Ich habe dieses Argument zum Testen privater Methoden nie als überzeugend empfunden. Sie können Code aus Gründen, die nichts mit der öffentlichen API einer Klasse zu tun haben, ständig in Methoden umgestalten. Es ist sehr schön, diese Methoden unabhängig voneinander testen zu können , ohne anderen Code zu benötigen . Das ist der Grund, warum Sie überarbeitet haben, oder? Um ein Stück unabhängige Funktionalität zu erhalten. Diese Funktionalität sollte auch unabhängig testbar sein.
Robert Harvey
Die Antwort von @RobertHarvey wurde mit einer Schimpfworte erweitert. "Private Methoden sind vorteilhaft für Unit-Tests ..."
Mücke
Ich verstehe, was Sie über private Methoden und Codeabdeckung sagen, aber ich habe nicht wirklich empfohlen, diese Methoden öffentlich zu machen, damit Sie sie testen können. Ich habe mich dafür ausgesprochen, sie unabhängig testen zu können, obwohl sie privat sind. Sie können weiterhin öffentliche Tests durchführen, die die private Methode berühren, wenn Sie möchten. Und ich kann meine privaten Tests haben.
Robert Harvey
@ Robert Harvey Ich verstehe. Schätze, es läuft auf den Codierungsstil hinaus. Bei der Menge an privaten Methoden, die ich normalerweise herstelle, und bei der Rate, mit der ich diese umgestalte, ist es einfach ein Luxus, mir keine Tests leisten zu können. Nicht-private API ist eine andere Sache, ich neige dazu , es zu sein , sich nur zögerlich und langsam zu modifizieren
gnat
Vielleicht sind Sie besser darin, Methoden zu schreiben, die beim ersten Mal funktionieren, als ich. Für mich muss ich die Methode testen, um sicherzustellen, dass sie zuerst funktioniert, bevor ich weitermachen kann, und es wäre umständlich und umständlich, sie indirekt durch Abfeuern einer Reihe anderer Methoden testen zu müssen.
Robert Harvey
9

Der wahrscheinlichste Grund ist: Es hat mit der Geschichte zu tun. Javas Vorfahr Oak hatte nur drei Zugriffsebenen: privat, geschützt, öffentlich.

Mit der Ausnahme, dass private in Oak das Äquivalent zu package private in Java war. Sie können Abschnitt 4.10 von Oaks Sprachspezifikation lesen (Hervorhebung meiner):

Standardmäßig sind alle Variablen und Methoden in einer Klasse (einschließlich Konstruktoren) privat. Auf private Variablen und Methoden kann nur von Methoden zugegriffen werden, die in der Klasse deklariert sind, und nicht von ihren Unterklassen oder anderen Klassen (mit Ausnahme von Klassen im selben Paket ).

Daher war der private Zugriff, wie in Java bekannt, ursprünglich nicht vorhanden. Wenn Sie jedoch mehr als ein paar Klassen in einem Paket haben, würde es zu einem Alptraum der Namenskollision führen, wenn Sie nicht privat sind, wie wir es jetzt kennen (z. B. hat java.until.concurrent fast 60 Klassen) stellte es vor.

Die (ursprünglich als privat bezeichnete) Standard-Paketzugriffssemantik wurde jedoch zwischen Oak und Java nicht geändert.

Assylias
quelle
5

Ich glaube, dass bei ordnungsgemäßer Verwendung des Standardzugriffs die Testbarkeit verbessert wird, während die Kapselung beibehalten wird. Außerdem wird der Modifikator für den privaten Zugriff überflüssig.

Es gibt Situationen, in denen man zwei getrennte Klassen haben möchte, die enger miteinander verbunden sind, als alles öffentlich zugänglich zu machen. Dies hat ähnliche Vorstellungen von "Freunden" in C ++, bei denen eine andere Klasse, die als "Freund" einer anderen Klasse deklariert ist, auf ihre privaten Mitglieder zugreifen kann.

Wenn Sie sich die Innereien von Klassen wie BigInteger ansehen, finden Sie eine Reihe von Standardschutzpaketen für Felder und Methoden (alles, was in der Liste als blaues Dreieck angezeigt wird). Auf diese Weise können andere Klassen im Paket java.math für bestimmte Operationen optimaler auf ihre Innereien zugreifen (diese Methoden werden in BigDecimal aufgerufen - BigDecimal wird von einer BigInteger-Zahl unterstützt und speichert die erneute Implementierung von BigInteger . Dies ist die Paketebene. Isn ' Dies ist ein Schutzproblem, da java.math ein versiegeltes Paket ist und keine weiteren Klassen hinzugefügt werden können.

Auf der anderen Seite gibt es viele Dinge, die tatsächlich privat sind, wie sie sein sollten. Die meisten Pakete sind nicht versiegelt. Ohne private könnte Ihr Mitarbeiter eine andere Klasse in dieses Paket einfügen und auf das (nicht mehr) private Feld zugreifen und die Kapselung aufheben.

Es ist zu beachten, dass Unit-Tests nicht in Betracht gezogen wurden, als die Schutzbereiche gebaut wurden. JUnit (das XUnit-Testframework für Java) wurde erst 1997 entwickelt (eine Geschichte dazu finden Sie unter http://www.martinfowler.com/bliki/Xunit.html ). Die Alpha- und Betaversionen des JDK waren 1995 und JDK 1.0 war 1996, obwohl die Schutzstufen erst mit JDK 1.0.2 wirklich festgelegt wurden (vorher konnte man eine private protectedSchutzstufe haben).

Einige würden argumentieren, dass der Standard nicht Paketebene, sondern privat sein sollte. Andere argumentieren, dass es keinen Standardschutz geben sollte, aber alles sollte explizit angegeben werden - einige Anhänger davon werden Code schreiben wie:

public class Foo {
    /* package */ int bar = 42;
    ...
}

Beachten Sie den Kommentar dort.

Der wahre Grund, warum der Schutz auf Paketebene der Standard ist, geht wahrscheinlich an den Design-Hinweisen von Java verloren (ich habe nachgegraben und kann keine Warum- Artikel finden, viele Leute erklären den Unterschied, aber keiner sagt "das ist der Grund" ").

Also rate ich mal. Und das ist alles, was es ist - eine Vermutung.

Zunächst versuchten die Leute immer noch, das Sprachdesign herauszufinden. Sprachen haben seitdem aus Javas Fehlern und Erfolgen gelernt. Dies könnte als Fehler aufgeführt werden, dass nicht für alle Felder etwas definiert werden muss.

  • Die Designer sahen ein gelegentliches Bedürfnis nach einer "Freund" -Beziehung von C ++.
  • Die Designer wollten explizite Aussagen für publicund privateda diese die größten Auswirkungen auf den Zugang haben.

Somit ist privat nicht voreingestellt und alles andere ist in Ordnung. Der Standardbereich wurde als Standard beibehalten. Dies war möglicherweise der erste Bereich, der erstellt wurde, und wurde im Interesse einer strikten Abwärtskompatibilität mit älterem Code so belassen.

Wie gesagt, das sind alles nur Vermutungen .


quelle
Ah, wenn nur die Friend-Funktion auf individueller Basis auf Mitglieder angewendet werden könnte, dann könnte man sagen, dass someFunction () nur von Klasse X gesehen werden kann ... das würde die Verkapselung wirklich verschärfen!
NewLogic
Warten Sie, Sie können dies in C ++ tun, wir brauchen dies in Java!
NewLogic
2
@ user1037729 Es sorgt für eine engere Kopplung zwischen den beiden Klassen. Die Klasse mit der Methode muss nun die anderen Klassen kennen, die ihre Freunde sind. Dies verstößt tendenziell gegen die Designphilosophie, die Java bietet. Die Menge der Probleme, die mit Freunden, aber nicht mit Schutz auf Paketebene gelöst werden können, ist nicht so groß.
2

Die Kapselung ist eine Art zu sagen, "darüber muss man nicht nachdenken".

                 Access Levels
Modifier    Class   Package  Subclass   World
public        Y        Y        Y        Y
protected     Y        Y        Y        N
no modifier   Y        Y        N        N
private       Y        N        N        N

Diese in nichttechnische Begriffe umsetzen.

  1. Öffentlichkeit: Jeder muss darüber nachdenken.
  2. Geschützt: Standard und Unterklassen müssen darüber Bescheid wissen.
  3. Standardmäßig, wenn Sie an dem Paket arbeiten, wissen Sie Bescheid (es befindet sich direkt vor Ihren Augen).
  4. Privat müssen Sie nur darüber Bescheid wissen, wenn Sie an dieser Klasse arbeiten.
jmoreno
quelle
Ich glaube nicht, dass es so etwas wie eine Privatklasse oder eine geschützte Klasse gibt.
Koray Tugay
@KorayTugay: Besuchen Sie docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html . Die innere Klasse ist privat. Lesen Sie den letzten Absatz.
Jmoreno
0

Ich glaube nicht, dass sie sich auf das Testen konzentriert haben, nur weil das Testen damals nicht sehr verbreitet war.

Was ich denke, dass sie erreichen wollten, war Kapselung auf der Paketebene. Wie Sie wissen, kann eine Klasse über interne Methoden und Felder verfügen und nur einen Teil davon durch öffentliche Mitglieder zugänglich machen. Ebenso kann ein Paket interne Klassen, Methoden und Felder enthalten und nur einen Teil davon verfügbar machen. Wenn Sie so denken, hat ein Paket eine interne Implementierung in einer Reihe von Klassen, Methoden und Feldern sowie eine öffentliche Schnittstelle in einer anderen (möglicherweise teilweise überlappenden) Reihe von Klassen, Methoden und Feldern.

Die erfahrensten Programmierer, die ich kenne, denken so. Sie nehmen die Kapselung und wenden sie auf eine Abstraktionsebene an, die über der Klasse liegt: ein Paket oder eine Komponente, wenn Sie möchten. Es scheint mir vernünftig, dass die Designer von Java bereits so ausgereift waren, wenn man bedenkt, wie gut Java noch mithält.

Alexander Torstling
quelle
Sie haben Recht, dass automatisierte Tests dort nicht sehr verbreitet waren. Aber es ist ein unausgereiftes Design.
Tom Hawtin - Tackline