7

Bei meinem alten C++ - Job haben wir immer große Sorgfalt darauf verwendet, Membervariablen einzukapseln und sie nur dann als Eigenschaften verfügbar zu machen, wenn sie absolut notwendig sind. Wir hätten sehr spezifische Konstruktoren, die sicherstellen, dass Sie das Objekt vollständig konstruieren, bevor Sie es verwenden.Verkapselung im Zeitalter von Frameworks

Heutzutage, mit ORM-Frameworks, Abhängigkeitsinjektion, Serialisierung, usw. scheint es, als ob Sie besser auf den Standardkonstruktor angewiesen sind und alles über Ihre Klasse in Eigenschaften offen legen, so dass Sie Dinge injizieren können. oder Objekte dynamischer erstellen und füllen.

In C# wurde mit Objektinitialisierern, die Ihnen die Möglichkeit geben, Ihren eigenen Konstruktor zu definieren, ein weiterer Schritt gemacht. (Ich weiß, Objekt Initialisierungen sind nicht wirklich benutzerdefinierte Konstruktoren, aber ich hoffe, Sie bekommen meinen Punkt.)

Gibt es irgendwelche allgemeinen Bedenken mit dieser Richtung? Es scheint, als würde die Verkapselung zugunsten der Bequemlichkeit weniger wichtig werden.

EDIT: Ich weiß, dass Sie Mitglieder immer noch sorgfältig einkapseln können, aber ich fühle mich wie wenn Sie versuchen, einige Klassen zu öffnen, müssen Sie entweder sitzen und sorgfältig darüber nachdenken, wie jedes Mitglied zu kapseln, oder einfach nur aussetzen als eine Eigenschaft, und sorgen Sie sich darüber, wie es später initialisiert wird. Es scheint einfach der einfachste Weg heutzutage zu sein, Dinge als Eigenschaften zu exponieren und nicht so vorsichtig zu sein. Vielleicht bin ich einfach falsch, aber das war nur meine Erfahrung, vor allem mit den neuen C# -Sprachenfunktionen.

+0

Lassen Sie sich von niemandem täuschen, diese Objektinitialisierer sind nur syntaktischer Zucker. – BobbyShaftoe

+0

Ich weiß, dass Objektinitialisierer nicht wirklich Konstruktoren sind, aber das ist nicht mein Punkt. Sie ermöglichen es Ihnen, ein Objekt mit dem Standardkonstruktor zu konstruieren und Eigenschaften von Hand festzulegen. –

+0

Nicht wirklich sicher, welcher Unterschied zwischen denen ist und nur jede Anweisung eingeben, um jede Eigenschaft festzulegen. Aber ok. – BobbyShaftoe

Antwort

3

Ich stimme Ihrer Schlussfolgerung nicht zu. Es gibt viele gute Möglichkeiten, in C# mit allen oben genannten Technologien zu kapseln, um gute Programmierpraktiken zu erhalten. Ich würde auch sagen, dass es darauf ankommt, wessen Technologiedemo du ansiehst, aber am Ende kommt es darauf an, den Zustandsraum deiner Objekte zu reduzieren, so dass du sicherstellen kannst, dass sie ihre Invarianten jederzeit behalten.

Take Objekt relationalen Frameworks; die meisten von ihnen erlauben Ihnen, anzugeben, wie sie die Entitäten hydratisieren werden; NHibernate zum Beispiel können Sie so sagen access="property" oder access="field.camelcase" und ähnliches. Dies ermöglicht Ihnen, Ihre Eigenschaften zu kapseln.

funktioniert auf den anderen Typen, die Sie haben, meist diejenigen, die keine Entitäten sind, obwohl Sie AOP + ORM + IOC in einigen sehr schönen Möglichkeiten kombinieren können, um den Zustand dieser Dinge zu verbessern. IoC wird oft aus Schichten über Ihren Domain-Entities verwendet, wenn Sie eine datengesteuerte Anwendung erstellen, was ich denke, Sie sind, da Sie über ORMs sprechen.

Sie ("sie" sind Anwendungs- und Domänendienste und andere dem Programm innewohnende Klassen) offenbaren ihre Abhängigkeiten, können aber tatsächlich in noch besserer Isolation gekapselt und getestet werden als zuvor seit den Paradigmen von Design-by-Contract/Design -by-interface, das Sie häufig verwenden, wenn Sie Abhängigkeiten in Schein-basierten Tests (in Verbindung mit IoC) verspotten, wird Sie in Richtung "Semantik" von Klassen zu Komponenten bewegen. Ich meine: jede Klasse, wenn sie mit dem obigen aufgebaut wird, wird besser gekapselt.

für urig Aktualisiert: Dies gilt für beide Belichtungs konkrete Abhängigkeiten und Belichtungs Schnittstellen. Zunächst zu Schnittstellen: Was ich oben andeutete war, dass Dienste und andere Anwendungsklassen, die Abhängigkeiten haben, mit OOP von Verträgen/Schnittstellen und nicht von spezifischen Implementierungen abhängig sind. In C/C++ und älteren Sprachen gab es nicht die Schnittstelle und abstrakte Klassen können nur so weit gehen. Mit Schnittstellen können Sie verschiedene Runtime-Instanzen an die gleiche Schnittstelle binden, ohne sich Gedanken über den ausgelaufenen internen Zustand machen zu müssen, von dem Sie beim Abstrahieren und Einkapseln wegzukommen versuchen. Mit abstrakten Klassen können Sie immer noch eine Klassenimplementierung bereitstellen, nur dass Sie sie nicht instanziieren können, aber die Erben müssen immer noch über die Invarianten in Ihrer Implementierung Bescheid wissen, die den Zustand durcheinander bringen können.

Zweitens, über konkrete Klassen als Eigenschaften: Sie müssen vorsichtig sein, welche Arten von Typen;) Sie als Eigenschaften aussetzen. Angenommen, Sie haben in Ihrer Instanz eine Liste; Stellen Sie IList dann nicht als Eigenschaft zur Verfügung. Dies wird wahrscheinlich undicht werden, und Sie können nicht garantieren, dass Verbraucher der Schnittstelle keine Dinge hinzufügen oder Dinge entfernen, auf die Sie angewiesen sind; Stellen Sie stattdessen etwas wie IEnumerable und geben Sie eine Kopie der Liste oder noch besser, tun Sie es als Methode: public IEnumerable MyCollection {get {return _List.Enum(); }} und Sie können 100% sicher sein, sowohl die Leistung als auch die Kapselung zu erhalten. Niemand kann IEnumerable hinzufügen oder entfernen, und Sie müssen immer noch keine teure Array-Kopie ausführen. Die entsprechende Hilfsmethode:

static class Ext { 
    public static IEnumerable<T> Enum<T>(this IEnumerable<T> inner) { 
     foreach (var item in inner) yield return item; 
    } 
} 

So, während Sie nicht zu 100% Verkapselung in etwa erhalten können mit dem Erstellen überlastet ist gleich Betreiber/Methode, die Sie schließen mit dem öffentlichen Schnittstellen zu bekommen.

Sie können auch die neuen Funktionen von .Net 4.0, die auf SpeC# basieren, verwenden, um die oben genannten Verträge zu überprüfen.

Serialisierung wird immer da sein und ist seit langer Zeit. Zuvor wurde es vor dem Internet-Bereich zum Speichern des Objektgraphen auf Diskette für den späteren Abruf verwendet, jetzt wird es in Webdiensten, in Kopiersemantik und beim Weiterleiten von Daten an z.B. ein Browser. Dies bricht die Kapselung nicht notwendigerweise, wenn Sie einige [NonSerialized] -Attribute oder die Entsprechungen für die richtigen Felder angeben.

Objektinitialisierer sind nicht identisch mit Konstruktoren, sie sind nur eine Möglichkeit, ein paar Codezeilen zu reduzieren. Werte/Instanzen in der {} werden erst zugewiesen, wenn alle Konstruktoren ausgeführt wurden. Im Prinzip ist das also genau so, wie wenn keine Objektinitialisierer verwendet werden.

Ich denke, was Sie beachten müssen, ist von den guten Prinzipien, die Sie aus Ihrem vorherigen Job gelernt haben, abzuweichen und sicherzustellen, dass Sie Ihre Domain-Objekte mit Business-Logik hinter gute Schnittstellen und dito für Ihren Dienst eingekapselt halten -Schicht.

+0

Hallo Henrik, Können Sie ein Beispiel dafür geben, wie "Sie ihre Abhängigkeiten offen legen, aber tatsächlich in noch besserer Isolation eingekapselt und getestet werden können als zuvor?" Wenn eine Klasse ihre Abhängigkeiten in öffentlichen Eigenschaften verfügbar macht, wie kann dies die Kapselung nicht verletzen? Danke! urig – urig

+0

Wenn ich zurückblicke, sehe ich, dass ich verschwommen war in dem, was ich meinte. – Henrik

0

Ich denke, dass die Kapselung ist immer noch wichtig, es hilft mehr in Bibliotheken als alles imho. Sie können eine Bibliothek erstellen, die X ausführt, aber Sie müssen nicht alle wissen, wie X erstellt wurde. Und wenn Sie es genauer erstellen wollten, um die Art, wie Sie X erstellen, zu verschleiern. Wie ich über Kapselung gelernt habe, erinnere ich auch daran, dass Sie Ihre Variablen immer als privat definieren sollten, um sie vor Datenangriffen zu schützen. Zum Schutz gegen einen Hacker, der Ihren Code bricht und auf Variablen zugreift, die er nicht verwenden soll.

1

Private Mitglieder sind immer noch unglaublich wichtig. Der Zugriff auf interne Objektdaten ist immer gut und sollte nicht ignoriert werden.

Viele Male private Methoden, die ich gefunden habe, um zu übertreiben. Meistens, wenn die Arbeit, die du machst, wichtig genug ist, um auszubrechen, kannst du es so umgestalten, dass entweder a) die private Methode trivial ist oder b) ein integraler Bestandteil anderer Funktionen ist.

Darüber hinaus, mit Unit-Tests, mit vielen Methoden private macht es sehr schwierig, Unit-Test. Es gibt Möglichkeiten (Freunde von Testobjekten usw.), aber fügen Schwierigkeiten hinzu.

Ich würde private Methoden jedoch nicht völlig abzählen.Jedes Mal, wenn es wichtige interne Algorithmen gibt, die außerhalb der Klasse keinen Sinn ergeben, gibt es keinen Grund, diese Methoden aufzudecken.