Sollten geschützt Attribute immer verboten werden?
Frage
Ich verwende Vererbung selten, aber wenn ich tun, ich benutze nie Attribute geschützt, weil ich denke, es ist die Verkapselung der geerbten Klassen bricht.
Haben Sie geschützte Attribute verwenden? was tun Sie sie für?
Lösung
In diesem Interview auf Design by Bill Venners, Joshua Bloch, den Autor von < em> Effective Java sagt:
Trusting Subklassen
Bill Venners: Sollte ich vertraue Subklassen inniger als Nicht-Subklassen? Zum Beispiel mache ich es einfacher für eine Unterklasse Umsetzung zu brechen mich als ich würde für eine Nicht-Unterklasse? Im Insbesondere, wie fühlen Sie sich über geschützte Daten?
Josh Bloch: , um etwas zu schreiben, das sowohl ableitbare und robust gegen eine bösartige Unterklasse ist eigentlich eine ziemlich schwierige Sache zu tun, Angenommen Sie haben die Unterklasse Zugang geben auf Ihre internen Datenstrukturen. Wenn die Unterklasse haben keinen Zugriff auf Alles, was ein normaler Benutzer nicht der Fall, dann ist es schwieriger für die Unterklasse zu tun Schaden. Aber es sei denn, Sie Machen Sie alle Ihre Methoden endgültig, die Unterklasse kann immer noch brechen Sie Ihre Verträge nur durch die falsche tun Dinge als Antwort auf Verfahren Aufruf. Das ist genau, warum die sicherheitskritische Klassen wie String Finale sind. Sonst könnte jemand eine Unterklasse schreiben, die Strings macht erscheinen wandelbar, welche wäre ausreichende Sicherheit zu brechen. so können Sie müssen Ihre Subklassen vertrauen. Wenn du nicht vertrauen ihnen, dann können Sie nicht zulassen, sie, weil Subklassen kann so leicht verursachen eine Klasse zu verletzen ihre Verträge.
Soweit geschützten Daten im Allgemeinen, es ist ein notwendiges Übel. Es sollte sein auf ein Minimum beschränkt. Die meisten geschützten Daten und geschützte Verfahren betragen Begehung einer Implementierung Detail. Ein geschütztes Gebiet ist ein Implementierung Detail, dass Sie Sichtbarmachung zu Subklassen. selbst ein geschützte Methode ist ein Stück interne Struktur, die Sie machen sichtbar zu Subklassen.
Der Grund, warum Sie es sichtbar zu machen, ist, dass ist es oft notwendig, um zu ermöglichen, Subklassen ihre Arbeit zu tun, oder zu tun es effizient. Aber sobald du getan hast es, sind Sie dazu verpflichtet. Ist das jetzt etwas, das Sie nicht erlaubt sind ändern, auch wenn Sie später mehr finden effiziente Implementierung, die keine mehr beinhaltet die Verwendung einer insbesondere Feld oder eine Methode.
So alle anderen Dinge gleich sind, Sie sollten keine geschützten Mitglieder überhaupt. Aber das heißt, wenn Sie zu haben wenige, dann können Sie Ihre Klasse nicht verwendet werden als Superklasse, oder zumindest nicht als eine effiziente Superklasse. oft Sie herauszufinden, nach der Tat. Meine Philosophie ist als einige geschützte Mitglieder haben als möglich, wenn Sie die erste schreiben Klasse. Dann versuchen Sie es zu Unterklasse. Sie herausfinden kann, dass ohne eine besondere geschützte Methode werden alle Subklassen müssen einige schlechte Sache tun.
Als Beispiel wenn man sich
AbstractList
, finden Sie, dass es finden ist ein geschütztes Verfahren ein löschen Bereich der Liste in einem Schuss (removeRange
). Warum ist dort das? Da das normale Idiom entfernen ein Bereich, bezogen auf die öffentliche API, ist zu rufensubList
eine UnterList
zu bekommen, und rufen Sie dannclear
auf, dass UnterList
. Ohne diese besondere geschützte Verfahren werden jedoch die einzigen Sache, dassclear
tun können, ist immer wieder einzelne Elemente entfernen.Denken Sie darüber nach. Wenn Sie ein Array Darstellung, was es tun? Es wird wiederholt, um die Anordnung zusammenbrechen, tun, um N Arbeit N-mal. So wird es nehmen eine quadratische Menge an Arbeit, statt der linearen Menge an Arbeit dass es sollte. Durch die Bereitstellung dieser geschützte Methode lassen wir jede Implementierung, die können effizient eine ganze Reihe löschen, dies zu tun. Und jede vernünftige
List
Implementierung kann löschen effizienter eine Reihe alles auf einmal.Dass wir müssten diese geschützt Methode ist etwas, das Sie haben würde sein Weg klüger als ich to wissen up Vorderseite. Grundsätzlich ist implementiert die Ding. Dann, als wir begannen, Unterklasse es haben wir erkannt, dass Bereich löschen war quadratisch. Wir konnten nicht leisten, so Ich habe in der geschützten Methode. Ich denke das ist der beste Ansatz mit geschützte Methoden. Setzen Sie in nur möglich, und dann mehr nach Bedarf hinzufügen. Geschützte Methoden darstellen Verpflichtungen Entwürfe, die Sie vielleicht ändern möchten. Sie können jederzeit hinzufügen geschützte Methoden, aber Sie können nicht nehmen sie aus.
Bill Venners:? Und geschützte Daten
Josh Bloch: Die gleiche, aber noch mehr. Geschützte Daten sind noch gefährlich in Bezug auf die Unordnung zu Ihrer Daten Invarianten. Wenn Sie jemanden geben sonst Zugriff auf einige interne Daten, sie haben freie Herrschaft über sie.
Kurzversion:. Es bricht Kapselung, aber es ist ein notwendiges Übel, das auf einem Minimum gehalten werden sollte,
Andere Tipps
C #:
Ich verwende für abstrakte oder virtuelle Methoden geschützt, die ich Basisklassen wollen außer Kraft zu setzen. Ich mache auch ein Verfahren geschützt, wenn sie von Basisklassen aufgerufen werden können, aber ich will nicht, dass es außerhalb der Klassenhierarchie genannt.
Sie können sie für statische müssen (oder ‚global‘) Attribut, das Sie mögen, dass Ihre Unterklassen oder Klassen aus demselben Paket (wenn es über Java ist) von profitieren.
Diese static final Attribute eine Art ‚konstanten Wert‘ darstellen, hat selten eine Getter-Funktion, so dass ein geschütztes statisches Attribut final Sinn in diesem Fall machen könnte.
Scott Meyers sagt nicht Verwendung geschützt Attribute in Effective C ++ (3. Aufl.):
Artikel 22:. Deklarieren private Datenelemente
Der Grund ist der gleiche Sie geben: es bricht Verkapselungen. Die Folge davon ist, dass ansonsten lokale Änderungen am Layout der Klasse könnten abhängige Typen brechen und in Veränderungen in vielen anderen Orten führen.
Es gibt nie gute Gründe geschützt Attribute zu haben. Eine Basisklasse muss in der Lage sein, auf Zustand abhängen, was bedeutet, den Zugriff auf Daten durch Accessormethoden einzuschränken. Sie können nicht, dass jemand Zugriff auf Ihre privaten Daten geben, auch Kinder.
Das protected
Schlüsselwort ist ein konzeptioneller Fehler und Sprache Design Pfusch und mehrere modernen Sprachen, wie Nim und Ceylon (siehe http://ceylon-lang.org/documentation/faq/language-design/#no_protected_modifier ), die sorgfältig entworfen wurde, und nicht nur das Kopieren häufige Fehler, don‘ t haben eine solche Schlüsselwort.
Es ist nicht Mitglieder geschützt, die Verkapselung bricht, ist es Mitglieder ausgesetzt wird, die nicht sein sollte ausgesetzt, die Verkapselung bricht ... es spielt keine Rolle, ob sie geschützt sind oder öffentlich. Das Problem mit protected
ist, dass es verbohrt und irreführend ... erklärt Mitglieder protected
(statt private
) ist sie nicht zu schützen, ist es das Gegenteil der Fall ist, genau wie public
tut. Ein geschütztes Mitglied, zugänglich außerhalb der Klasse zu sein, ist in der Welt ausgesetzt und so muss ihre Semantik für immer aufrecht erhalten werden, so wie es der Fall für public
ist. Die ganze Idee von „geschützt“ ist Unsinn ... Verkapselung ist nicht Sicherheit, und das Schlüsselwort fördert nur die Verwirrung zwischen den beiden. Sie können durch die Vermeidung alle Verwendungen von protected
in Ihren eigenen Klassen ein wenig helfen - wenn etwas ein interner Teil der Umsetzung ist, ist nicht Teil der Semantik der Klasse und kann sich in Zukunft ändern, dann machen es privat oder intern Ihr Paket, Modul, Montag, etc. Wenn es ein unveränderlicher Teil der Klasse Semantik ist, dann macht sie öffentlich, und dann werden Sie nicht Benutzer Ihrer Klasse ärgern, die, dass es ein nützliches Mitglied in der Dokumentation sehen können, aber‘ t verwenden es, wenn sie ihre eigenen Instanzen erstellen und können sie durch Subclassing erhalten.
Ich benutze keine geschützten Attribute in Java, weil sie dort nur Paket geschützt sind. Aber in C ++, werde ich sie in abstrakten Klassen verwenden, so dass die erbenden Klasse direkt zu übernehmen.
Im Allgemeinen wird nicht Sie wirklich wollen nicht geschützte Datenelemente verwenden. Dies ist doppelt wahr, wenn Sie eine API zu schreiben. Sobald jemand aus Ihrer Klasse erbt kann man nie wirklich Wartung tun und sie irgendwie nicht in einer seltsamen und manchmal wilden Art und Weise zu brechen.
Ich denke, geschützte Attribute eine schlechte Idee sind. Ich benutze Check diese Regel mit meinen Java-Entwicklungsteams zu erzwingen.
ich an einem Projekt vor kurzem arbeitet, waren das „geschützte“ Mitglied eine sehr gute Idee war. Die Klasse hiearchy war so etwas wie:
[+] Base
|
+--[+] BaseMap
| |
| +--[+] Map
| |
| +--[+] HashMap
|
+--[+] // something else ?
Die Basis implementiert eine std :: list aber sonst nichts. Der direkte Zugriff auf die Liste wurde dem Benutzer verboten, aber als die Basisklasse unvollständig war, berief sie sich ohnehin auf abgeleiteten Klassen, das indirection in die Liste zu implementieren.
Der Indirektionsoperator könnte aus mindestens zwei Varianten: std :: map und stdext :: hash_map. Beiden Karten werden auf die gleiche Weise verhalten, aber die Tatsache, muss die hash_map den Schlüssel sein hashable (in VC2003, gießbare size_t).
So BaseMap implementiert eine TMap als Templat-Typ, der eine Karte artige Behälter war.
Karte und HashMap waren zwei abgeleiteten Klassen von BaseMap, ein spezialisiert BaseMap auf std :: map, und die andere auf stdext :: hash_map.
So:
-
war Basis nicht als solches verwendbar (keine öffentlichen Accessoren!) Und nur gemeinsame Merkmale und Code zur Verfügung gestellt
-
BaseMap benötigt einfach zu lesen / schreiben, um eine std :: list
-
Karte und HashMap benötigten einfachen Lese- / Schreibzugriff auf die TMap definierte in BaseMap.
Für mich war die einzige Lösung für die std :: Liste und die Membervariablen TMap geschützt zu verwenden. Es gab keinen Weg ich diese „privaten“ setzen würde, weil ich sowieso alle aussetzen würde oder fast alle ihre Funktionen durch Lese- / Schreibzugriffs trotzdem.
Am Ende, ich denke, dass, wenn Sie en Sie Ihre Klasse in mehrere Objekte unterteilt, jede Ableitung benötigte Funktionen zu seiner Mutter Klasse hinzufügen, und nur die abgeleitete Klasse wirklich verwendbar sind, dann geschützt ist der Weg zu gehen. Die Tatsache, das „geschützte Mitglied“ war eine Klasse, und so war fast unmöglich zu „brechen“, geholfen.
Aber ansonsten geschützt sollten so weit wie möglich vermieden werden (d .: Verwendung standardmäßig privat und öffentlich, wenn Sie das Verfahren aussetzen müssen).
Ich benutze sie. Kurz gesagt, es ist ein guter Weg, wenn Sie einige Attribute geteilt haben wollen. Zugegeben, könnten Sie setzen / erhalten Funktionen für sie schreiben, aber wenn es keine Validierung ist, was ist dann der Punkt? Es ist auch schneller.
Bedenken Sie: Sie haben eine Klasse, die Ihre Basisklasse ist. Es hat einige Attribute, die Sie wan't in den untergeordneten Objekte zu verwenden. Sie könnten eine get / set-Funktion für jeden schreiben, oder Sie können sie gerade eingestellt haben.
Mein typisches Beispiel ist eine Datei / Stream-Handler. Sie wollen den Handler für den Zugriff auf (das heißt Dateideskriptor), aber Sie wollen es von anderen Klassen verstecken. Es ist viel einfacher, als einen Satz zu schreiben / get-Funktion für sie.
In der Regel ja. Eine geschützte Methode ist in der Regel besser.
Im Einsatz gibt es ein Maß an Einfachheit gegeben durch die Verwendung eines geschütztes letztes Variable für ein Objekt, das von allen Kindern einer Klasse geteilt wird. Ich habe immer davon abraten mit Primitiven oder Sammlungen verwenden, da die Verträge sind unmöglich für diese Typen zu definieren.
In letzter Zeit habe ich getrennte Sachen kommen Sie mit Primitiven und rohen Sammlungen von Sachen tun Sie mit gut ausgebildeten Klassen zu tun. Primitives und Sammlungen sollten immer privat sein.
Außerdem habe ich begonnen Variablen gelegentlich aussetzt öffentlichen Teil, wenn sie endgültig sind Gebenes und wohlgeformte Klassen sind, die nicht zu flexibel sind (auch hier nicht Primitiven oder Sammlungen).
Das ist nicht irgendeine dumme Abkürzung, dachte ich, es ist ziemlich ernst und entschied es gibt absolut keinen Unterschied zwischen einem öffentlich letzt Variable ein Objekt und einem Getter ausgesetzt wird.
Es hängt davon ab, was Sie wollen. Wenn Sie eine schnelle Klasse dann Daten wollen, sollten geschützt werden und die Verwendung geschützt und öffentliche Methoden. Weil ich denke, Sie sollten davon ausgehen, dass die Benutzer, die recht gut aus der Klasse kennen Ihre Klasse ableiten oder zumindest haben sie Ihr Handbuch an der Funktion, lesen sie gehen außer Kraft zu setzen.
Wenn Ihre Benutzer verwirren mit Ihrer Klasse es nicht Ihr Problem. Jeder böswilliger Benutzer kann die folgenden Zeilen hinzufügen, wenn einer Ihrer virtuals überschrieben:
(C #)
static Random rnd=new Random();
//...
if (rnd.Next()%1000==0) throw new Exception("My base class sucks! HAHAHAHA! xD");
//...
Sie können nicht jede Klasse abdichten, dies zu verhindern.
Natürlich, wenn Sie eine Einschränkung auf einige Ihrer Felder wollen, dann verwenden Accessorfunktionen oder Eigenschaften oder etwas, das Sie wollen und machen das Feld privat, weil es keine andere Lösung gibt ...
Aber ich persönlich nicht um jeden Preis an die OOP-Prinzipien halten mag. Insbesondere Eigenschaften mit dem einzigen Zweck zu machen, um mit privaten Datenelementen.
(C #):
private _foo;
public foo
{
get {return _foo;}
set {_foo=value;}
}
Das war meine persönliche Meinung.
Aber das tun, was Ihr Chef werden (wenn er private Felder will als das.)
Ich verwende geschützte Variablen / Attribute in Basisklassen, die ich weiß, ich habe nicht vor in den Methoden zum Ändern. Auf diese Weise haben Subklassen vollen Zugriff auf ihre geerbten Variablen und haben nicht die (künstlich geschaffene) Kopf gehen durch Getter / Setter auf sie zuzugreifen. Ein Beispiel ist eine Klasse, einen zugrundeliegenden I / O-Strom verwendet wird; gibt es wenig Grund direkten Zugang zum zugrunde liegenden Stream nicht Subklassen zu ermöglichen.
Dies ist für Membervariablen in Ordnung, die in direkter einfacher Weise innerhalb der Basisklasse und alle Unterklassen verwendet werden. Aber für eine Variable, die eine kompliziertere Verwendung hat (zum Beispiel den Zugriff auf verursacht es Nebenwirkungen in anderen Mitgliedern innerhalb der Klasse), eine direkt zugängliche Variable nicht geeignet ist. In diesem Fall kann es aus privatem und öffentlichem / protected Getter / Setter werden kann stattdessen zur Verfügung gestellt werden. Ein Beispiel hierfür ist ein interner Puffermechanismus durch die Basisklasse bereitgestellt, wobei die Puffer Zugriff direkt von einer Unterklasse, die Integrität der Algorithmen, die von der Basisklasse verwendet gefährden würde sie verwalten.
Es ist eine Design-Urteil Entscheidung, basierend auf, wie einfach die Membervariable ist, und wie es so in zukünftigen Versionen zu erwarten ist.
Die Einkapselung ist groß, aber es kann zu weit genommen werden. Ich habe Klassen, die eigenen Methoden zugegriffen seine Mitgliedsvariablen privat gesehen mit nur Getter / Setter-Methoden. Das ist übertrieben, denn wenn eine Klasse nicht über einen eigenen Methoden mit seinem eigenen privaten Daten vertrauen können, wer kann sie trauen?