Sollten Sie jemals geschützte Mitgliedsvariablen verwenden? Was sind die Vorteile und welche Probleme kann dies verursachen?
Sollten Sie jemals geschützte Mitgliedsvariablen verwenden? Was sind die Vorteile und welche Probleme kann dies verursachen?
Antworten:
Sollten Sie jemals geschützte Mitgliedsvariablen verwenden?
Kommt darauf an, wie wählerisch du bist, wenn du den Zustand versteckst.
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.
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.
no particular advantage over protected methods/properties
sein no particular advantage over *private* methods/properties
?
super
Konstrukt verwenden, um den übergeordneten Konstruktor aufzurufen. Anschließend werden private Statusvariablen in der übergeordneten Klasse initialisiert.
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.
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:
Indem ich die Variablen auf privat beschränke, kann ich das gewünschte Verhalten durch Setter oder Getter erzwingen.
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:
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.
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.
Legen Sie einen Haltepunkt fest oder fügen Sie eine Debug-Ausgabe hinzu, wenn die Variable gelesen oder beschrieben wird.
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.
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.
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.
protected
ist 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.
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.
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 .) "
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.