2009-09-29 6 views
6

Eintrag 23 von Effective C++ - Staaten: Bevorzugen Sie nicht-Mitglied Nicht-Freund-Funktionen zu Member-Funktionen."Pinnacle" der Verkapselung - Frage bezüglich der Empfehlung von effektivem C++

Der ganze Zweck des Artikels war, Kapselung, sowie Paket Flexibilität und funktionale Erweiterbarkeit zu ermutigen, aber meine Frage ist, wie weit gehen Sie, wenn es darum geht, diesen Ratschlag zu nehmen?

Zum Beispiel könnten Sie Ihre Klasse, Ihre privaten Datenmitglieder haben und dann einen minimalistischen Ansatz verfolgen, indem Sie öffentliche Funktionen auf nur Accessoren und/oder Mutatoren für Ihre privaten Datenmitglieder reduzieren. Dann könnte jede einzelne andere Funktion eine Nichtmitgliedsfunktion sein.

Aber wären Sie bereit, die Kapselung zu erhöhen, um die Codeklarheit mit Accessoren und Mutatoren auf der ganzen Linie zu opfern? Wo ist die Linie gezeichnet?

+4

Für diejenigen ohne eine Kopie von Effektiv C++, Meyers präsentiert ein ähnliches Argument unter http://www.ddj.com/cpp/184401197. –

Antwort

9

Erstens stimmt nicht jeder mit diesem Ratschlag überein. Ich glaube nicht, dass ich irgendjemanden außer Meyers gesehen habe (edit: und Herb Sutter) diesen Rat geben, und ich habe es nur im Zusammenhang mit C++ gesehen.Beispielsweise ist das Erstellen von Nichtmitglied-Nicht-Freund-Funktionen in Java oder C# nicht wirklich möglich, da Java und C# keine freien Funktionen haben und Ruby-Entwickler beispielsweise "humane interfaces" bevorzugen, die absichtlich Memberfunktionen erzeugen, die dasselbe tun was Nicht-Mitglieder tun könnten, um den Anrufern dieser Funktionen das Leben zu erleichtern. Wenn Sie den Rat von Meyers akzeptieren, sollten Sie Nicht-Mitglied-Nicht-Freund-Funktionen den Mitgliedsfunktionen vorziehen (und ich denke, es ist ein guter Rat, hat mir sicherlich geholfen, Kapselung besser anzuwenden, um die Implementierung einer Klasse zu kapseln.) sogar von seinen Mitgliedsfunktionen), das ist nur eine Designachse zu berücksichtigen. Das Schlüsselkonzept des objektorientierten Designs ist, dass Objekte etwas tun. Ein Objekt ist nicht einfach eine Tasche von Settern und Gettern, zu denen anderer Code stößt. Stattdessen sollte es ein angehängtes Verhalten haben - das heißt, es sollte Methoden haben, die Dinge tun - und es sollte die Details darüber kapseln, wie es diese Dinge macht. Wenn Sie diesen Ansatz bei OO befolgen, dann schmerzt der Rat von Meyers so sehr, wie Sie es getan haben, die Verkapselung, anstatt ihr zu helfen: Sie setzen schließlich alle internen Implementierungsvariablen der Klasse über Getter und Setter frei, anstatt sie so zu verstecken, dass nur die Klasse Methoden (der Code, der für Sachen im Namen der Klasse verantwortlich ist, der der einzige Grund ist, warum Sie eine Klasse haben, um damit zu beginnen), kann dazu kommen.

So Ihre Frage zu beantworten, wie weit Meyers Rat zu nehmen: Nicht unnötig wiederum Funktionen in Elementfunktionen, wenn sie vernünftigerweise als Nicht-Freund nicht-Member-Funktionen mit einer Klasse öffentliche Schnittstelle implementiert werden könnte, aber beschädige die öffentliche Schnittstelle einer Klasse nicht und verletze ihre Kapselung, indem du die Implementierung einfach der aussetzt, um etwas zu einem Mitglied zu machen. Und stellen Sie sicher, dass Sie die Kapselung gegen andere Bedenken und andere Ansätze ausbalancieren (einschließlich, wenn Ihr Team beschließt, diesen Weg zu gehen, die Vor- und Nachteile einer ausgewachsenen humanen Schnittstelle).

+2

Herb Sutter stimmt mit Meyers überein und bietet ein besseres Beispiel: http://www.gotw.ca/gotw/084.htm –

+0

Die Regeln werden offensichtlich auf so einfache Art und Weise angegeben, dass sie beim Leser bleiben, aber Ich denke, dass sie alle mit einem Körnchen Salz eingenommen werden müssen. Die Frage war offener und spiegelte nicht wirklich persönliche Gefühle wider.Ich dachte nur, dass es interessant ist, darüber nachzudenken, und dieser Beitrag hat das klargestellt, indem ich den Zweck von OOP geklärt habe (der Beitrag wurde mit C++ getaggt, also dachte ich, dass das in Betracht gezogen würde). P.S. Ich habe kein Team, ich bin nicht einmal berechtigt, Vollzeit zu arbeiten. – trikker

+0

Alexander Stepanov scheint auch Nichtmitgliedsfunktionen zu bevorzugen, indem er Freundfunktionen den Elementfunktionen empfiehlt. –

0

Im Allgemeinen ist die Idee hinter den Accessoren und Mutatoren, dass die Variable in irgendeiner Weise geschützt werden muss. Wenn die Variable vertrauliche Informationen enthält, die nur von einer Entität aktualisiert werden sollen, können Sie diese Überprüfung in Ihren Mutator einschließen. Die einfache Tatsache ist, dass die meisten Accessoren und Mutatoren einfach eine Formalität sind (so dass C# jetzt automatische Eigenschaften hat) und nicht notwendig sind. Die Quintessenz ist, verwenden Sie Ihr Urteil. Wenn es einen eingeschränkten Zugriff benötigt, fügen Sie einen Mutator hinzu. Wenn es egal ist, wer die Variable bekommt/setzt, werden der Accessor und der Mutator nicht benötigt.

+1

Ich würde sagen, dass man die internen Variablen einer Klasse niemals offen legen sollte. Wenn Sie jemals Code debuggen, instrumentieren oder refaktorieren müssen, ist es viel einfacher (und klarer), den Accessor einfach zu modifizieren, als alle Benutzer zu modifizieren, die jetzt einen Accessor aufrufen. Nur meine 2 Cent. – Vitali

3

Machen Sie einen Schritt zurück und betrachten die Zweck der Klasse: Was ein Job ist, es zu tun? Welche Klasseninvarianten müssen dafür sorgen, dass dieser eine Job optimal ausgeführt wird? Welche Rolle spielen Subclassing und Overriding im Klassenzweck?

Es ist definitiv nicht geeignet alles im Hinblick auf Zugriffs- und Mutatoren zu werfen: Das wäre fast Scheidung die Verbindung von Staat und Verhalten, das an der Wurzel der OOP ist, oder Maske Verhalten, das in keiner vernünftigen Gestaltung des Problems ist, Attribute unter dem Schleier solcher "vorgetäuschten" Mutatoren und Accessoren zu bekommen oder zu setzen.

Klassen mit nur Zugriffs- und Mutatoren sind ein Sonderfall - vielleicht einen Schritt nach oben von den traditionellen C-Art struct s durch einige Invarianten erhalten zu können, aber „gerade noch“ ;-).

meisten Klassen in einem guten OOP-Design werden das Verhalten haben - und so sehr ich generische Programmierung mag, ein Grund, C++ zu verwenden, ist die starke Mischung aus mehreren Paradigmen, unter denen OOP ausgemerzt nicht sein müssen! -)

2

Eigentlich nur Accessors und Mutatoren für die privaten Variablen Ihrer Klasse wäre in der Tat nicht minimalistisch (oder vielleicht minimalistisch in einem Sinne, aber nicht im "relevantesten" Sinne), wie Sie jetzt eine allgemeinere Schnittstelle präsentieren zur Welt. Die Idee der Einkapselung ist, dass die Schnittstelle Ihrer Klasse so eingeschränkt wie möglich sein sollte während Client-Code, um die Arbeit erledigt zu ermöglichen.

Dieser Ansatz macht es einfacher, die zugrundeliegende Implementierung in der Zukunft zu ändern, die der Punkt der Kapselung in erster Linie ist.

0

Die Arbeit, wie man Dinge in Einheiten von richtiger Größe verpackt, ist eine subjektive Kunst. Ein Problem, das ich habe, wenn ich etwas Nicht-Nicht-Freund mache, ist, dass, wenn eine Änderung der internen Struktur erfordert, neue öffentliche Schnittstellen hinzugefügt werden müssen, um bestehende Nicht-Mitglieder zu unterstützen, oder dass das Nicht-Mitglied jetzt als anerkannt wird ein Freund, der eine engere Verbindung zeigt, als zuvor geplant. Es kann nun teuer werden, Änderungen vorzunehmen.

Während auf einer logischen Ebene gesagt werden kann, dass eine Nichtmitgliedsfunktion mit der Klasse gepackt werden kann und die Schnittstelle der Klasse bildet, muss der Entwickler Code ändern, wenn sich der Speicherort einer Methode ändert, und Daher ist die Implementierung der Schnittstelle nicht fehlerfrei in ihrer Verwendung.

Eines dieser netten Dinge über Eiffel ist, dass auf eine Methode ohne Parameter genauso zugegriffen wird wie auf eine Variable, so dass der Wechsel zwischen diesen Elementen transparent ist.

Verwandte Themen