Sollten Sie jemals geschützte Mitgliedsvariablen verwenden?

97

Sollten Sie jemals geschützte Mitgliedsvariablen verwenden? Was sind die Vorteile und welche Probleme kann dies verursachen?

John Channing
quelle

Antworten:

73

Sollten Sie jemals geschützte Mitgliedsvariablen verwenden?

Kommt darauf an, wie wählerisch du bist, wenn du den Zustand versteckst.

  • Wenn Sie nicht möchten, dass der interne Status verloren geht, sollten Sie alle Ihre Mitgliedsvariablen als privat deklarieren.
  • Wenn Sie sich nicht wirklich darum kümmern, dass Unterklassen auf den internen Status zugreifen können, ist protected gut genug.

Wenn ein Entwickler vorbeikommt und Ihre Klasse unterordnet, kann er es vermasseln, weil er es nicht vollständig versteht. Mit Ausnahme der öffentlichen Benutzeroberfläche können private Mitglieder die implementierungsspezifischen Details der Vorgehensweise nicht sehen, sodass Sie sie später flexibel ändern können.

Allain Lalonde
quelle
1
Können Sie die Leistung geschützter Variablen gegenüber einer privaten Variablen mit einer get / set-Methode kommentieren?
Jake
3
Ich würde sagen, es lohnt sich nicht, sich Sorgen zu machen, es sei denn, Sie stellen durch Profilerstellung fest, dass der Flaschenhals letztendlich die Accessoren sind (was es fast nie ist). Es gibt Tricks, die durchgeführt werden können, um die JIT in Bezug auf Dinge intelligenter zu machen, wenn dies letztendlich ein Problem darstellt. In Java können Sie beispielsweise darauf hinweisen, dass der Accessor eingebunden werden kann, indem Sie ihn als endgültig markieren. Ehrlich gesagt ist die Leistung von Gettern und Setzern weitaus weniger wichtig als der Umgang mit der Systemorganisation und den tatsächlichen Leistungsproblemen, die von einem Profiler ermittelt werden.
Allain Lalonde
23
@Jake: Sie sollten niemals Entwurfsentscheidungen treffen, die auf Leistungsannahmen basieren. Sie treffen Designentscheidungen basierend auf dem, was Sie für das beste Design halten. Nur wenn Ihre Profilerstellung im wirklichen Leben einen Engpass in Ihrem Design aufweist, können Sie diesen beheben. Wenn das Design solide ist, ist die Leistung normalerweise auch gut.
Mecki
Mit privaten Mitgliedern außer der öffentlichen Schnittstelle können sie die implementierungsspezifischen Details nicht sehen. Sie können einfach die Klasse öffnen und nachschlagen, also macht das keinen Sinn?!
Black
2
@Black Allain bedeutete eindeutig, dass sie nicht auf diese Mitglieder zugreifen können und daher keinen Code gegen sie erstellen können, sodass der Autor der Klasse die geschützten Mitglieder später entfernen / ändern kann. (Natürlich würde die Pimpl-Sprache es ermöglichen, sie visuell und auch vor den Übersetzungseinheiten einschließlich des Headers zu verbergen.)
underscore_d
31

Das allgemeine Gefühl heutzutage ist, dass sie eine unangemessene Kopplung zwischen abgeleiteten Klassen und ihren Basen verursachen.

Sie haben keinen besonderen Vorteil gegenüber geschützten Methoden / Eigenschaften (früher hatten sie möglicherweise einen leichten Leistungsvorteil), und sie wurden auch häufiger in einer Zeit verwendet, in der eine sehr tiefe Vererbung in Mode war, was derzeit nicht der Fall ist.

Will Dean
quelle
2
Sollte nicht no particular advantage over protected methods/propertiessein no particular advantage over *private* methods/properties?
Penghe Geng
Nein, da ich über die Vor- und Nachteile verschiedener Arten der Kommunikation zwischen abgeleiteten Klassen und ihren Grundlagen spreche / sprach - all diese Techniken wären "geschützt" - besteht der Unterschied darin, ob es sich um Mitgliedsvariablen (Felder) oder Eigenschaften / Methoden handelt ( dh Unterprogramme irgendeiner Art).
Will Dean
1
Danke für die schnelle Klarstellung. Ich bin froh, die Antwort des Originalplakats in einer Stunde auf meine Frage zu einem 6 Jahre alten Beitrag zu haben. Sie glauben nicht, dass dies in den meisten anderen Online-Foren passieren kann :)
Penghe Geng
9
Noch bemerkenswerter ist, dass ich in dieser Zeitspanne tatsächlich mit mir selbst einverstanden bin ...
Will Dean
Eine Aufgabe eines Konstruktors besteht darin, dafür zu sorgen, dass alle Zustandsvariablen explizit initialisiert werden. Wenn Sie diese Konvention einhalten, können Sie das superKonstrukt verwenden, um den übergeordneten Konstruktor aufzurufen. Anschließend werden private Statusvariablen in der übergeordneten Klasse initialisiert.
ncmathsadist
31

Wenn etwas nicht absichtlich als öffentlich gedacht ist, mache ich es im Allgemeinen privat.

Wenn eine Situation auftritt, in der ich von einer abgeleiteten Klasse Zugriff auf diese private Variable oder Methode benötige, ändere ich sie von privat in geschützt.

Das passiert so gut wie nie - ich bin wirklich überhaupt kein Fan von Vererbung, da es keine besonders gute Möglichkeit ist, die meisten Situationen zu modellieren. Auf jeden Fall weitermachen, keine Sorge.

Ich würde sagen, dass dies für die Mehrheit der Entwickler in Ordnung ist (und wahrscheinlich der beste Weg, dies zu tun).

Die einfache Tatsache ist , dass wenn ein anderer Entwickler ein Jahr später vorbeikommt und entscheidet, dass er Zugriff auf Ihre private Mitgliedsvariable benötigt, er einfach den Code bearbeitet, ihn in "geschützt" ändert und sein Geschäft fortsetzt.

Die einzigen wirklichen Ausnahmen sind, wenn Sie binäre DLLs in Black-Box-Form an Dritte versenden. Dies besteht im Wesentlichen aus Microsoft, diesen Anbietern von "Custom DataGrid Control" und möglicherweise einigen anderen großen Apps, die mit Erweiterungsbibliotheken geliefert werden. Wenn Sie nicht in dieser Kategorie sind, lohnt es sich nicht, Zeit und Mühe zu investieren, um sich über solche Dinge Gedanken zu machen.

Orion Edwards
quelle
8

Das Hauptproblem für mich ist, dass Sie, sobald Sie eine Variable geschützt haben, keiner Methode in Ihrer Klasse erlauben können, sich darauf zu verlassen, dass ihr Wert innerhalb eines Bereichs liegt, da eine Unterklasse sie immer außerhalb des Bereichs platzieren kann.

Wenn ich beispielsweise eine Klasse habe, die Breite und Höhe eines renderbaren Objekts definiert, und diese Variablen geschützt, kann ich keine Annahmen über (zum Beispiel) das Seitenverhältnis treffen.

Kritisch kann ich nie diese Annahmen an jedem Punkt von dem Moment an , dass Code als Bibliothek freigegeben ist, denn selbst wenn ich meine Setter aktualisieren Seitenverhältnis beizubehalten, ich habe keine Garantie, dass die Variablen über die Menge über die Setter oder zugegriffen werden Getter im vorhandenen Code.

Auch kann sich keine Unterklasse meiner Klasse für diese Garantie entscheiden, da sie auch die Variablenwerte nicht erzwingen kann, selbst wenn dies der gesamte Punkt ihrer Unterklasse ist .

Als Beispiel:

  • Ich habe eine Rechteckklasse, in der Breite und Höhe als geschützte Variablen gespeichert werden.
  • Eine offensichtliche Unterklasse (in meinem Kontext) ist eine "DisplayedRectangle" -Klasse, bei der der einzige Unterschied darin besteht, dass ich die Breiten und Höhen auf gültige Werte für eine grafische Anzeige beschränke.
  • Dies ist jetzt jedoch unmöglich , da meine DisplayedRectangle-Klasse diese Werte nicht wirklich einschränken kann , da jede Unterklasse die Werte direkt überschreiben kann, während sie weiterhin als DisplayedRectangle behandelt wird.

Indem ich die Variablen auf privat beschränke, kann ich das gewünschte Verhalten durch Setter oder Getter erzwingen.

Deworde
quelle
7

Im Allgemeinen würde ich Ihre geschützten Mitgliedsvariablen auf den seltenen Fall beschränken, in dem Sie die vollständige Kontrolle über den Code haben, der sie ebenfalls verwendet. Wenn Sie eine öffentliche API erstellen, würde ich niemals sagen. Im Folgenden wird die Mitgliedsvariable als "Eigenschaft" des Objekts bezeichnet.

Folgendes kann Ihre Oberklasse nicht tun, nachdem eine Mitgliedsvariable eher geschützt als privat mit Zugriffsberechtigten ist:

  1. Erstellen Sie träge einen Wert im laufenden Betrieb, wenn die Eigenschaft gelesen wird. Wenn Sie eine geschützte Getter-Methode hinzufügen, können Sie den Wert träge erstellen und zurückgeben.

  2. wissen, wann die Eigenschaft geändert oder gelöscht wurde. Dies kann zu Fehlern führen, wenn die Oberklasse Annahmen über den Status dieser Variablen trifft. Wenn Sie eine geschützte Setter-Methode für die Variable erstellen, bleibt diese Kontrolle erhalten.

  3. Legen Sie einen Haltepunkt fest oder fügen Sie eine Debug-Ausgabe hinzu, wenn die Variable gelesen oder beschrieben wird.

  4. Benennen Sie diese Mitgliedsvariable um, ohne den gesamten Code zu durchsuchen, der sie möglicherweise verwendet.

Im Allgemeinen denke ich, dass es der seltene Fall ist, dass ich empfehlen würde, eine geschützte Mitgliedsvariable zu erstellen. Es ist besser, ein paar Minuten damit zu verbringen, die Eigenschaft durch Getter / Setter freizulegen, als Stunden später einen Fehler in einem anderen Code aufzuspüren, der die geschützte Variable geändert hat. Darüber hinaus sind Sie gegen das Hinzufügen zukünftiger Funktionen (z. B. verzögertes Laden) versichert, ohne den abhängigen Code zu beschädigen. Es ist schwieriger, es später zu tun als jetzt.

Michael Bishop
quelle
7

Auf der Entwurfsebene mag es angebracht sein, eine geschützte Eigenschaft zu verwenden, aber für die Implementierung sehe ich keinen Vorteil darin, diese einer geschützten Mitgliedsvariablen zuzuordnen, anstatt Accessor / Mutator-Methoden.

Geschützte Mitgliedsvariablen haben erhebliche Nachteile, da sie dem Clientcode (der Unterklasse) effektiv den Zugriff auf den internen Status der Basisklassenklasse ermöglichen. Dies verhindert, dass die Basisklasse ihre Invarianten effektiv beibehält.

Aus dem gleichen Grund erschweren geschützte Elementvariablen das Schreiben von sicherem Multithread-Code erheblich, es sei denn, dies ist garantiert konstant oder auf einen einzelnen Thread beschränkt.

Accessor / Mutator-Methoden bieten erheblich mehr API-Stabilität und Implementierungsflexibilität bei der Wartung.

Wenn Sie ein OO-Purist sind, arbeiten Objekte zusammen, indem sie Nachrichten senden und nicht den Status lesen / festlegen.

Im Gegenzug bieten sie nur sehr wenige Vorteile. Ich würde sie nicht unbedingt aus dem Code eines anderen entfernen, aber ich benutze sie nicht selbst.

richj
quelle
4

Meistens ist es gefährlich, protected zu verwenden, da Sie die Kapselung Ihrer Klasse etwas aufbrechen, was durchaus durch eine schlecht gestaltete abgeleitete Klasse aufgeschlüsselt werden könnte.

Aber ich habe ein gutes Beispiel: Nehmen wir an, Sie können eine Art generischen Container verwenden. Es hat eine interne Implementierung und interne Accessoren. Sie müssen jedoch mindestens 3 öffentlichen Zugriff auf die Daten anbieten: map, hash_map, vector-like. Dann haben Sie so etwas wie:

template <typename T, typename TContainer>
class Base
{
   // etc.
   protected
   TContainer container ;
}

template <typename Key, typename T>
class DerivedMap     : public Base<T, std::map<Key, T> >      { /* etc. */ }

template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }

template <typename T>
class DerivedVector  : public Base<T, std::vector<T> >        { /* etc. */ }

Ich habe diese Art von Code vor weniger als einem Monat verwendet (der Code stammt also aus dem Speicher). Nach einigem Nachdenken glaube ich, dass der generische Base-Container zwar eine abstrakte Klasse sein sollte, auch wenn er recht gut leben kann, da die direkte Verwendung von Base so schmerzhaft wäre, dass er verboten werden sollte.

Zusammenfassung Sie haben also Daten geschützt, die von der abgeleiteten Klasse verwendet werden. Dennoch müssen wir berücksichtigen, dass die Basisklasse abstrakt sein sollte.

paercebal
quelle
Die Kapselung wird nicht mehr unterbrochen als bei öffentlichen Mitgliedern. Es ist eine Einstellung, die besagt, dass abgeleitete Klassen den Status der Klasse verwenden können, der Benutzern der Klasse nicht zugänglich ist.
Gbjbaanb
@gbjbaanb: Sie widersprechen sich selbst "es bricht die Kapselung nicht mehr als öffentliche Mitglieder" unterscheidet sich von "[nur] abgeleitete Klassen können den Klassenstatus verwenden". "geschützt" ist die Mitte zwischen öffentlich und privat. Also "geschützt [...] bricht etwas die Kapselung" ist immer noch wahr ...
paercebal
In der C ++ - Sprache machen Containeradapter wie std :: stack das zugrunde liegende Containerobjekt mit einer geschützten Variablen namens "c" verfügbar.
Johannes Schaub - litb
Ich weiß, dass dieser Beitrag ziemlich alt ist, aber ich habe das Bedürfnis, mich einzuschalten. Sie brechen die Kapselung nicht "etwas", Sie brechen sie vollständig. protectedist nicht mehr eingekapselt als public. Ich bin bereit, mich als falsch zu erweisen. Alles was Sie tun müssen, ist eine Klasse mit einem geschützten Mitglied zu schreiben und mir zu verbieten, sie zu ändern. Offensichtlich muss die Klasse nicht endgültig sein, da der Sinn der Verwendung von protected in der Vererbung liegt. Entweder ist etwas gekapselt oder nicht. Es gibt keinen Zwischenzustand.
Taekahn
3

Kurz gesagt, ja.

Geschützte Mitgliedsvariablen ermöglichen den Zugriff auf die Variable von allen Unterklassen sowie von allen Klassen im selben Paket. Dies kann insbesondere für schreibgeschützte Daten sehr nützlich sein. Ich glaube jedoch nicht, dass sie jemals notwendig sind, da jede Verwendung einer geschützten Mitgliedsvariablen unter Verwendung einer privaten Mitgliedsvariablen und einiger Getter und Setter repliziert werden kann.

Jay Stramel
quelle
1
Umgekehrt werden auch private Mitgliedsvariablen niemals benötigt. Öffentlichkeit ist für jede Nutzung ausreichend.
Alice
3

Nur zur Veranschaulichung, unter Punkt 24 von "Exceptional C ++", in einer der Fußnoten, sagt Sutter "Sie würden niemals eine Klasse schreiben, die eine öffentliche oder geschützte Mitgliedsvariable hat. Richtig? (Ungeachtet des schlechten Beispiels, das von einigen Bibliotheken festgelegt wurde .) "

hAcKnRoCk
quelle
2

Ausführliche Informationen zu .Net-Zugriffsmodifikatoren finden Sie hier

Geschützte Mitgliedsvariablen haben keine wirklichen Vor- oder Nachteile. Es geht darum, was Sie in Ihrer spezifischen Situation benötigen. Im Allgemeinen ist es gängige Praxis, Mitgliedsvariablen als privat zu deklarieren und den Zugriff von außen über Eigenschaften zu ermöglichen. Einige Tools (z. B. einige O / R-Mapper) erwarten außerdem, dass Objektdaten durch Eigenschaften dargestellt werden, und erkennen keine öffentlichen oder geschützten Elementvariablen. Wenn Sie jedoch wissen, dass Ihre Unterklassen (und NUR Ihre Unterklassen) auf eine bestimmte Variable zugreifen sollen, gibt es keinen Grund, sie nicht als geschützt zu deklarieren.

Manu
quelle
Der Wunsch, dass Unterklassen auf eine Variable zugreifen, unterscheidet sich stark von dem Wunsch, dass sie diese frei mutieren können. Dies ist eines der Hauptargumente gegen geschützte Variablen: Jetzt kann Ihre Basisklasse keine ihrer Invarianten mehr annehmen, da jede abgeleitete Klasse mit den geschützten Mitgliedern absolut alles tun kann. Das ist das Hauptargument gegen sie. Wenn sie nur auf die Daten zugreifen müssen , dann ... schreiben Sie einen Accessor. : P (Ich verwende geschützte Variablen, obwohl wahrscheinlich mehr als ich sollte, und ich werde versuchen, zu reduzieren!)
underscore_d