Privat vs Geschützt - Sichtbarkeit Good-Practice-Anliegen [geschlossen]

145

Ich habe gesucht und kenne den theoretischen Unterschied.

  • public - Jede Klasse / Funktion kann auf die Methode / Eigenschaft zugreifen.
  • protected - Nur diese Klasse und alle Unterklassen dürfen auf die Methode / Eigenschaft zugreifen.
  • privat - Nur diese Klasse kann auf die Methode / Eigenschaft zugreifen. Es wird nicht einmal vererbt.

Das ist alles in Ordnung und gut, die Frage ist, was ist der praktische Unterschied zwischen ihnen? Wann würden Sie verwenden privateund wann würden Sie verwenden protected? Gibt es eine Standard- oder akzeptable bewährte Methode für diese?

Um das Konzept der Vererbung und des Polymorphismus beizubehalten, verwende ich bis jetzt publicalles, auf das von außen zugegriffen werden soll (wie Konstruktoren und Hauptklassenfunktionen), und protectedinterne Methoden (Logik, Hilfsmethoden usw.). Bin ich auf dem richtigen Weg?

(Beachten Sie, dass diese Frage für mich ist, aber auch zum späteren Nachschlagen, da ich eine Frage wie diese SO nicht gesehen habe).

Madaras Geist
quelle
33
Ist das wichtig? Jede Sprache mit OOP-Unterstützung hat dieses Problem. Ich programmiere zufällig in PHP, aber ich denke, die Frage gilt für jede OOP-unterstützende Sprache.
Madaras Geist
2
Okay, fair genug, ich frage mich nur, ob du vergessen hast zu markieren. Jetzt sehe ich das oop-Tag.
Paul Bellora
Ich muss die Sichtbarkeit (privat / öffentlich / geschützt) für jede Eigenschaft der Klasse festlegen. Oder müssen nur einige von ihnen einen Sichtbarkeitstyp haben? Wenn ja, wie kann man entscheiden, für welche Immobilie eine erstklassige Sichtbarkeit erforderlich ist?
user4271704
1
Auf den ersten Blick scheint der Unterschied zwischen geschützt und privat offensichtlich. Verwenden Sie protected, wenn Unterklassen die Methode / Variable verwenden, andernfalls private. Insbesondere wenn Unterklassen eine sehr ähnliche private Variable im übergeordneten Element neu definieren müssten, machen Sie sie einfach geschützt.
iPherian
Es gibt einen weiteren Zugriffsspezifizierer internalin C # -Sprache, der die Zugriffsebene einer Klasse oder ihrer Mitglieder innerhalb einer physischen Assembly einschränkt. Ich bin mir jedoch nicht sicher über die Unterstützung oder ähnliches in anderen Sprachen.
RBT

Antworten:

128

Nein, Sie sind nicht auf dem richtigen Weg. Eine gute Faustregel lautet: Machen Sie alles so privat wie möglich. Dadurch wird Ihre Klasse gekapselter und die Interna der Klasse können geändert werden, ohne dass sich dies auf den Code Ihrer Klasse auswirkt.

Wenn Sie Ihre Klasse so gestalten, dass sie vererbbar ist, wählen Sie sorgfältig aus, was von Unterklassen überschrieben und zugänglich sein kann, und machen Sie diese geschützt (und endgültig, wenn Sie von Java sprechen möchten, wenn Sie sie zugänglich, aber nicht überschreibbar machen möchten). Beachten Sie jedoch, dass dieses Feld oder diese Methode Teil der öffentlichen API der Klasse ist und möglicherweise später nicht geändert wird, ohne Unterklassen zu beschädigen, sobald Sie akzeptieren, dass Unterklassen Ihrer Klasse vorhanden sind und ein geschütztes Feld oder eine geschützte Methode vorhanden ist.

Eine Klasse, die nicht vererbt werden soll, sollte endgültig gemacht werden (in Java). Sie können einige Zugriffsregeln (privat bis geschützt, endgültig bis nicht endgültig) für Unit-Tests lockern, sie dann aber dokumentieren und klarstellen, dass die Methode zwar geschützt ist, aber nicht überschrieben werden darf.

JB Nizet
quelle
1
Die eigentliche Frage hier ist: privatevs protectedWann soll eine Eigenschaft vererbt werden und wann nicht? Ich kann nicht wirklich sagen, ob ein Benutzer manchmal in der Zukunft meine Klasse nehmen und erweitern möchte ...
Madaras Geist
16
Nun, die Frage ist nicht , was der Benutzer will außer Kraft zu setzen, ist die Frage , was Sie wollen damit sein , außer Kraft gesetzt. Normalerweise hilft es, die Seite zu wechseln und zu überlegen: Wenn ich diese Klasse verwenden und eine Unterklasse erstellen würde, was möchte ich überschreiben können? Manchmal werden andere Benutzer immer noch Dinge vermissen ...
Thorsten Dittmar
3
Geschützte Felder sind eine schlechte Praxis bei der Vererbung! . Bitte hüte dich davor. Hier ist warum
RBT
4
Private Felder sind in der Praxis in der Vererbung schlecht!. (übertrieben) Bitte lesen Sie meine Antwort.
Larry
6
Typische Kontrollfreak-Meinung.
Bruno Desthuilliers
58

Lassen Sie mich dies vorwegnehmen, indem Sie sagen, dass ich hier hauptsächlich über den Methodenzugriff spreche und in etwas geringerem Maße Klassen als endgültig und nicht als Mitgliederzugriff markiere .

Die alte Weisheit

"Markiere es als privat, es sei denn, du hast einen guten Grund, es nicht zu tun."

machte Sinn in Tagen, als es geschrieben wurde, bevor Open Source den Entwicklerbibliotheksraum und VCS / Dependency mgmt dominierte. wurde dank Github, Maven usw. hyperkooperativ. Damals gab es auch Geld zu verdienen, indem die Art und Weise, wie eine Bibliothek genutzt werden konnte, eingeschränkt wurde. Ich habe wahrscheinlich die ersten 8 oder 9 Jahre meiner Karriere damit verbracht, mich strikt an diese "Best Practice" zu halten.

Heute halte ich es für einen schlechten Rat. Manchmal gibt es ein vernünftiges Argument, um eine Methode als privat oder als Klassenfinale zu markieren, aber es ist äußerst selten, und selbst dann verbessert es wahrscheinlich nichts.

Hast du jemals:

  • Sie waren enttäuscht, überrascht oder verletzt von einer Bibliothek usw., die einen Fehler hatte, der mit Vererbung und wenigen Codezeilen hätte behoben werden können, aber aufgrund privater / endgültiger Methoden und Klassen gezwungen waren, auf einen offiziellen Patch zu warten, der möglicherweise nie kommen würde? Ich habe.
  • Wollten Sie eine Bibliothek für einen etwas anderen Anwendungsfall verwenden, als von den Autoren vorgestellt, konnten dies jedoch aufgrund privater / endgültiger Methoden und Klassen nicht? Ich habe.
  • Sie wurden enttäuscht, überrascht oder verletzt von einer Bibliothek usw., deren Erweiterbarkeit zu freizügig war? Ich habe nicht.

Dies sind die drei größten Rationalisierungen, die ich für das standardmäßige Markieren von Methoden gehört habe:

Rationalisierung Nr. 1: Es ist unsicher und es gibt keinen Grund, eine bestimmte Methode zu überschreiben

Ich kann nicht zählen, wie oft ich mich geirrt habe, ob es jemals notwendig sein wird, eine bestimmte von mir geschriebene Methode zu überschreiben. Nachdem ich an mehreren populären Open-Source-Bibliotheken gearbeitet hatte, lernte ich auf die harte Tour die wahren Kosten für das Markieren von privaten Dingen. Oft entfällt die einzig praktikable Lösung für unvorhergesehene Probleme oder Anwendungsfälle. Umgekehrt habe ich es in mehr als 16 Jahren beruflicher Entwicklung nie bereut, eine aus Gründen der API-Sicherheit geschützte statt private Methode markiert zu haben. Wenn ein Entwickler eine Klasse erweitert und eine Methode überschreibt, sagt er bewusst: "Ich weiß, was ich tue." und aus Gründen der Produktivität sollte das ausreichen. Zeitraum. Wenn es gefährlich ist, notieren Sie es in der Klasse / Methode Javadocs, schlagen Sie die Tür nicht einfach blind zu.

Standardmäßig geschützte Markierungsmethoden sind eine Abschwächung für eines der Hauptprobleme in der modernen SW-Entwicklung: ein Versagen der Vorstellungskraft.

Rationalisierung Nr. 2: Sie hält die öffentliche API / Javadocs sauber

Dieser ist vernünftiger und je nach Zielgruppe vielleicht sogar das Richtige, aber es lohnt sich zu überlegen, wie hoch die Kosten für die "Sauberkeit" der API tatsächlich sind: Erweiterbarkeit. Aus den oben genannten Gründen ist es wahrscheinlich sinnvoller, für alle Fälle standardmäßig geschützte Dinge zu markieren.

Rationalisierung Nr. 3: Meine Software ist kommerziell und ich muss ihre Verwendung einschränken.

Dies ist auch vernünftig, aber als Verbraucher würde ich mich jedes Mal für den weniger restriktiven Wettbewerber entscheiden (vorausgesetzt, es bestehen keine signifikanten Qualitätsunterschiede).

Sag niemals nie

Ich sage nicht, niemals Methoden als privat zu markieren. Ich sage, die bessere Faustregel lautet: "Methoden schützen, es sei denn, es gibt einen guten Grund, dies nicht zu tun".

Dieser Rat ist am besten für diejenigen geeignet, die an Bibliotheken oder größeren Projekten arbeiten, die in Module unterteilt wurden. Bei kleineren oder monolithischeren Projekten spielt dies keine Rolle, da Sie ohnehin den gesamten Code kontrollieren und es einfach ist, die Zugriffsebene Ihres Codes zu ändern, wenn Sie ihn benötigen. Selbst dann würde ich immer noch den gleichen Rat geben :-)

Nick
quelle
Wrt your Rationalization # 1- Wenn ich etwas als geschützt markiere, entscheide ich mich ausdrücklich dafür, da ich der Meinung bin, dass dieses Verhalten nicht endgültig ist und möglicherweise ein Fall ist, in dem meine Kinderklassen es überschreiben möchten. Wenn ich mir nicht so sicher bin, möchte ich es standardmäßig privat machen. Insbesondere in einer Sprache wie C #, in der eine Methode standardmäßig nicht virtuell bleibt, macht es keinen Sinn, nur einen geschützten Zugriffsspezifizierer hinzuzufügen. Sie müssen sowohl virtualals auch protectedSchlüsselwörter hinzufügen , um Ihre Absicht sehr deutlich zu machen. Außerdem bevorzugen die Leute die Assoziation gegenüber der Vererbung, protectedda die Standardeinstellung schwer zu erkennen ist
RBT
4
Die Kombination von Schlüsselwörtern, die erforderlich sind, um eine Methode überschreibbar zu machen, ist meiner Meinung nach ein Sprachdetail. Das Gegenargument, das ich der Rationalisierung Nr. 1 vorlege, zielt genau auf den Fall ab, in dem Sie "Wenn ich nicht so sicher bin ..." erwähnen. Wenn Sie sich praktisch keinen Grund vorstellen können, warum dies gefährlich wäre, können Sie praktisch mehr erreichen, wenn Sie sich für die Erweiterbarkeit entscheiden. Wenn Sie "Assoziation" sagen, verstehe ich das als Synonym für Komposition. In diesem Fall sehe ich es nicht so oder so als wichtig an.
Nick
3
Ich erinnere mich nicht, wie oft ich die zugrunde liegenden Klassen "klonen" musste, nur weil ich 1 oder 2 oder die Methoden überschreiben wollte. Und wie Sie bereits sagten, ist es angesichts des sozialen Trends, dass wir mehr Code als je zuvor teilen, viel sinnvoller, standardmäßig mit geschützt als privat zu arbeiten. dh offener für deine eigene Logik sein.
Shinkou
1
Zustimmen. Ich wollte nur Javas BufferedReader optimieren, um benutzerdefinierte Zeilenbegrenzer zu verarbeiten. Dank privater Felder muss ich die gesamte Klasse klonen, anstatt sie einfach zu erweitern und readLine () zu überschreiben.
Ron Dunn
21

Hör auf, private Felder zu missbrauchen !!!

Die Kommentare hier scheinen die Verwendung privater Felder überwiegend zu unterstützen. Nun, dann habe ich etwas anderes zu sagen.

Sind private Felder grundsätzlich gut? Ja. Aber zu sagen, dass eine goldene Regel darin besteht, alles privat zu machen, wenn Sie sich nicht sicher sind, ist definitiv falsch ! Sie werden das Problem erst sehen, wenn Sie auf eines stoßen. Meiner Meinung nach sollten Sie Felder als geschützt markieren, wenn Sie sich nicht sicher sind .

Es gibt zwei Fälle, in denen Sie eine Klasse erweitern möchten:

  • Sie möchten einer Basisklasse zusätzliche Funktionen hinzufügen
  • Sie möchten eine vorhandene Klasse ändern, die sich außerhalb des aktuellen Pakets befindet (in einigen Bibliotheken möglicherweise).

An privaten Feldern ist im ersten Fall nichts auszusetzen. Die Tatsache, dass Leute private Felder missbrauchen , macht es so frustrierend, wenn Sie herausfinden, dass Sie Scheiße nicht ändern können.

Stellen Sie sich eine einfache Bibliothek vor, die Autos modelliert:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

Der Bibliotheksautor dachte: Es gibt keinen Grund, warum die Benutzer meiner Bibliothek auf die Implementierungsdetails von assembleCar()richtig zugreifen müssen . Markieren wir die Schraube als privat.

Nun, der Autor ist falsch. Wenn Sie nur die assembleCar()Methode ändern möchten, ohne die gesamte Klasse in Ihr Paket zu kopieren, haben Sie kein Glück. Sie müssen Ihr eigenes screwFeld neu schreiben . Nehmen wir an, dieses Auto verwendet ein Dutzend Schrauben, und jede von ihnen enthält einen nicht trivialen Initialisierungscode in verschiedenen privaten Methoden, und diese Schrauben sind alle als privat gekennzeichnet. An diesem Punkt beginnt es zu saugen.

Ja, Sie können mit mir argumentieren, dass der Bibliotheksautor besseren Code hätte schreiben können, sodass an privaten Feldern nichts auszusetzen ist . Ich behaupte nicht, dass das private Feld ein Problem mit OOP ist . Es ist ein Problem, wenn Leute sie benutzen.

Die Moral der Geschichte lautet: Wenn Sie eine Bibliothek schreiben, wissen Sie nie, ob Ihre Benutzer auf ein bestimmtes Feld zugreifen möchten. Wenn Sie sich nicht sicher sind, markieren Sie es, protecteddamit alle später glücklicher sind. Zumindest nicht das private Feld missbrauchen .

Ich unterstütze Nicks Antwort sehr.

Larry
quelle
2
Die Verwendung privater Felder bedeutet meistens, dass Vererbung die falsche Abstraktion ist, um ein bestimmtes Verhalten einer Klasse / eines Objekts zu ändern (wenn es bewusst verwendet wird). Das Ändern verstößt normalerweise stillschweigend gegen LSP, da das Verhalten geändert wird, ohne dass sich die API ändert. Tolle Antwort, +1.
Madaras Geist
Predigen! Privat ist der Fluch meiner Existenz, wenn ich versuche, Minecraft-Mods zu schreiben. Nichts ist ärgerlicher, als Reflection oder ASM verwenden zu müssen, um das zu beheben.
RecursiveExceptionException
Wie ich Nicks Antwort verstand, bezog er sich hauptsächlich auf Methoden, nicht auf Eigenschaften. Ich stimme voll und ganz zu, dass wir Methoden nicht jedes Mal als privat deklarieren sollten und die Verwendung dieser Methoden nachdenklich sein muss, aber warum sollten Sie empfehlen, Eigenschaften als privat zu deklarieren, nur weil Sie die Klasse einfacher "umschreiben" möchten. Teilen Sie Ihre Frustration voll und ganz mit, wenn ich täglich mit 3rd Ps arbeite, aber ermutigen wir die Leute, eine solide Architektur zu erstellen und nicht nur zu sagen: "Der Code wäre am Ende Mist, also haben wir ihn von Anfang an geschützt, damit wir ihn leicht umschreiben können." Obwohl ich deine Frustration teile.
sean662
16

Ich habe vor einiger Zeit einen Artikel gelesen, in dem es darum ging, jede Klasse so weit wie möglich zu sperren. Machen Sie alles endgültig und privat, es sei denn, Sie müssen sofort einige Daten oder Funktionen der Außenwelt zugänglich machen. Es ist immer einfach, den Geltungsbereich zu erweitern, um später zulässiger zu werden, aber nicht umgekehrt. Betrachten wir zuerst so viele Dinge wie möglich zu machen , finaldie zwischen der Wahl machen privateund protectedviel einfacher.

  1. Machen Sie alle Klassen endgültig, es sei denn, Sie müssen sie sofort in Unterklassen unterteilen.
  2. Machen Sie alle Methoden endgültig, es sei denn, Sie müssen sie sofort unterordnen und überschreiben.
  3. Machen Sie alle Methodenparameter endgültig, es sei denn, Sie müssen sie im Hauptteil der Methode ändern, was ohnehin meistens etwas umständlich ist.

Wenn Sie eine Abschlussklasse haben, machen Sie alles privat, es sei denn, die Welt braucht unbedingt etwas - machen Sie es öffentlich.

Wenn Sie eine Klasse mit Unterklassen haben, überprüfen Sie alle Eigenschaften und Methoden sorgfältig. Überlegen Sie zunächst, ob Sie diese Eigenschaft / Methode überhaupt Unterklassen zugänglich machen möchten. Wenn Sie dies tun, prüfen Sie, ob eine Unterklasse Ihr Objekt zerstören kann, wenn sie den Eigenschaftswert oder die Methodenimplementierung beim Überschreiben durcheinander bringt. Wenn es möglich ist und Sie die Eigenschaft / Methode Ihrer Klasse auch vor Unterklassen schützen möchten (klingt ironisch, ich weiß), machen Sie sie privat. Ansonsten machen Sie es geschützt.

Haftungsausschluss: Ich programmiere nicht viel in Java :)

Anurag
quelle
2
Zur Verdeutlichung: A final classin Java ist eine Klasse, von der nicht geerbt werden kann. A final methodist nicht virtuell, dh nicht von einer Unterklasse überschreibbar (Java-Methoden sind standardmäßig virtuell). A final field/parameter/variableist eine unveränderliche Variablenreferenz (in dem Sinne, dass sie nach der Initialisierung nicht mehr geändert werden kann).
M. Stramm
1
Komischerweise habe ich den genau entgegengesetzten Rat gesehen, dass nichts endgültig sein sollte, es sei denn, es ist sicher, dass das Überschreiben der fraglichen Sache das Potenzial hat, eine Invariante zu brechen. Auf diese Weise kann jeder Ihren Unterricht nach Bedarf erweitern.
GordonM
Können Sie einen Fall in Ihrer Programmierkarriere nennen, in dem das Markieren eines Methodenparameters "final" Ihr Verständnis beeinflusst hat? (außer wenn vom Compiler gefordert)
user949300
7

Wann würden Sie verwenden privateund wann würden Sie verwenden protected?

Private Vererbung kann eher als Beziehung als als IS-A- Beziehung implementiert betrachtet werden . Einfach ausgedrückt, die externe Schnittstelle der ererbenden Klasse hat keine (sichtbare) Beziehung zur geerbten Klasse. Sie verwendet die Vererbung nur, um eine ähnliche Funktionalität zu implementieren, die die Basisklasse bereitstellt.private

Im Gegensatz zur privaten Vererbung ist die geschützte Vererbung eine eingeschränkte Form der Vererbung, bei der die abgeleitete Klasse IS-A der Basisklasse ist und der Zugriff der abgeleiteten Mitglieder nur auf die abgeleitete Klasse beschränkt werden soll.

Alok Speichern
quelle
2
Das "In Bezug auf die Beziehung implementiert" ist eine HAS-A-Beziehung. Wenn alle Attribute privat sind, können Sie nur über Methoden darauf zugreifen. Dies ist dasselbe, als ob die Unterklasse ein Objekt der Oberklasse enthalten würde. Wenn Sie alle geschützten Attribute aus Ihren konkreten Klassen entfernen, müssen Sie auch alle Vererbungen von ihnen entfernen.
Shawnhcorey
2
Interessanter Denkprozess - Private Inheritance . @shawnhcorey danke, dass Sie deutlich gemacht haben, dass es auf eine HAS-A- Beziehung (auch bekannt als Assoziation) hinweist (die Aggregation oder Komposition sein kann). Ich bin der Meinung, dass dieser Beitrag auch aktualisiert werden sollte, um dies zu verdeutlichen.
RBT
1

Nun, es geht nur um die Kapselung, wenn die Paybill-Klassen die Abrechnung von Zahlungen abwickeln, dann in der Produktklasse, warum sollte der gesamte Abrechnungsprozess benötigt werden, dh die Zahlungsmethode, wie zu zahlen ist, wo zu zahlen ist. Lassen Sie also nur das, was für andere Klassen und Objekte verwendet wird nichts weiter als diese Öffentlichkeit für diejenigen, die auch andere Klassen verwenden würden, geschützt für diese Begrenzung nur für die Erweiterung von Klassen. Da Sie Madara Uchiha sind, ist das Private so, wie "limboo"Sie es sehen können (Sie unterrichten nur eine Klasse).

ujwal dhakal
quelle