Wie wird die Kapselung aus Sicherheitsgründen verwendet?

8

Ich lerne OOP. Ich habe viel über Kapselung gelernt, aber je mehr ich las, desto verwirrter wurde ich.

Ich verstehe, dass wir Daten verbergen (indem wir sie privat machen) und sie dem Benutzer der Klasse (anderen Entwicklern) als Eigenschaften oder Methoden zur Verfügung stellen. Ich verstehe auch, dass wir unter Kapselung Details verbergen.

In einem Artikel (http://www.csharp-station.com/Tutorial/CSharp/lesson19) las ich:

Auszug aus dem Artikel

Wenn Sie ein Objekt entwerfen, müssen Sie darüber nachdenken, wie andere es verwenden könnten. Im besten Fall wäre jedes Programm, das das Objekt verwendet, gut gestaltet und der Code würde sich niemals ändern. Die Realität ist jedoch, dass sich Programme häufig ändern und in einer Teamumgebung viele Menschen zu der einen oder anderen Zeit denselben Code berühren. Daher ist es vorteilhaft zu überlegen, was schief gehen könnte, sowie das makellose Bild davon, wie das Objekt verwendet werden sollte .

Untersuchen Sie im Fall des BankAccount-Objekts die Situation, in der Code außerhalb Ihres Objekts auf ein dezimales Betragsfeld oder ein Zeichenfolgenfeld "Kundenname" zugreifen kann. Zum Zeitpunkt des Schreibens des Codes würde alles gut funktionieren. Später im Entwicklungszyklus stellen Sie jedoch fest, dass das BankAccount-Objekt eine int-Kunden-ID verfolgen sollte und nicht die Zeichenfolge "Kundenname", da Sie keine Beziehungen zwischen Informationen duplizieren möchten (oder einen anderen gültigen Grund, um die Definition des internen Status zu ändern). . Solche Änderungen verursachen einen Welleneffekt in Ihrem Code, da er für die Verwendung der ursprünglich entworfenen BankAccount-Klasse erstellt wurde (wobei CustomerName eine Zeichenfolge ist). Sie müssen jetzt den Code ändern, der in Ihrer gesamten Anwendung auf diesen Status zugreift.

Das objektorientierte Prinzip der Kapselung hilft, solche Probleme zu vermeiden, und ermöglicht es Ihnen, den internen Status und den abstrakten Zugriff darauf durch Typelemente wie Methoden, Eigenschaften und Indexer zu verbergen. Durch die Kapselung können Sie die Kopplung zwischen Objekten verringern und die Wartbarkeit Ihres Codes verbessern.

Frage Wie hilft die Kapselung bei Änderungen am Code und seinen Auswirkungen? Wenn ich für ein Datenelement seinen Typ von int in float ändere (auch wenn ich dies mit der Eigenschaft using verfügbar mache), muss ich den Variablentyp ändern, wenn ich diesen Code bereits verwende.

Bitte leiten Sie mich, wie die Kapselung bei solchen Änderungen hilft.

Vielen Dank für diese Hilfe und Anleitung.

haansi
quelle

Antworten:

8

Wie hilft die Kapselung bei Änderungen am Code und seinen Auswirkungen? Wenn ich für ein Datenelement seinen Typ von int in float ändere (auch wenn ich dies mit der Eigenschaft using verfügbar mache), muss ich den Variablentyp ändern, wenn ich diesen Code bereits verwende.

Der Vorteil der Kapselung besteht darin, dass Sie die interne Implementierung ändern können, ohne den Clientcode zu beschädigen. Es schützt Sie nicht, wenn Sie entscheiden, dass Sie die Schnittstelle zu Ihrem Code ändern müssen, aber das ist eine andere Sache.

Beispiel: Angenommen, Sie haben einen Wert, der den Preis pro Einheit einer Ware darstellt. Der Preis wird in Cent ausgedrückt, und da Sie nicht in Bruchteilen von Cent handeln, haben Sie beschlossen, die Eigenschaft zu einer Ganzzahl zu machen (ich werde hier C verwenden, weil ich mit C # nicht sehr vertraut bin):

int _price

int pricePerUnit(void) {
    return _price;
}

int priceForUnits(int units) {
    return units * _price;
}

Das alles funktioniert gut, bis eines Tages jemand bemerkt, dass Ihre Firma aufgrund von Rundungsfehlern viel Geld verliert. Viele der Waren, die Sie verfolgen, werden in vielen tausend Einheiten gekauft und verkauft. Sie müssen also damit beginnen, den Preis mit einer Genauigkeit von mindestens 0,001 Cent zu verfolgen. Da Sie klug genug waren, den Preis zu kapseln, anstatt Kunden direkt darauf zugreifen zu lassen, können Sie diese Änderung ziemlich schnell vornehmen:

double _dprice

int pricePerUnit(void) {
    return (int)_dprice;
}

int priceForUnits(int units) {
    return (int)(units * _dprice);
}

Die Schnittstelle, über die Kunden Preise erhalten, bleibt unverändert, aber die Daten, die sie zurückerhalten, sind jetzt genauer. Wenn der Preis pro Einheit 1,001 USD beträgt, priceForUnits(1000000)wird jetzt ein Preis zurückgegeben, der 1000 USD höher ist als zuvor. Dies geschieht, obwohl Sie die Schnittstelle zu Ihrem System überhaupt nicht geändert haben und daher keinen Client-Code beschädigt haben.

Das ist möglicherweise nicht immer alles, was Sie tun müssen. Manchmal müssen Sie Ihre Benutzeroberfläche ändern oder erweitern, damit Sie den Preis auch den Kunden genauer melden können:

double pricePerUnit() {
    return _dprice;
}

Bei einer solchen Änderung wird der Clientcode beschädigt, sodass Sie stattdessen die alte Schnittstelle beibehalten und eine neuere, bessere Routine bereitstellen können:

int pricePerUnit() {
    return (int)_dprice;
}

double accuratePricePerUnit() {
    return _dprice;
}

Sie und der Rest Ihres Teams können dann mit der Konvertierung aller Clients Ihres Systems beginnen, um die neueren, besseren zu verwenden accuratePricePerUnit(). Der Client-Code wird genauer, wenn Sie bei dieser Aufgabe Fortschritte machen, aber auch das alte Zeug sollte weiterhin so funktionieren wie in der Vergangenheit.

Der Punkt ist jedenfalls, dass Sie mit der Kapselung die Funktionsweise der Interna ändern können, während Sie eine konsistente Benutzeroberfläche präsentieren, und dass Sie nützliche Änderungen vornehmen können, ohne anderen Code zu beschädigen. Es schützt Sie nicht immer davor, anderen Code aktualisieren zu müssen, aber es kann Ihnen zumindest dabei helfen, dies auf kontrollierte Weise zu tun.

Caleb
quelle
3

Nach meiner Erfahrung macht die Kapselung das "Falsche" viel schwieriger. Sie können Funktionen, die semantisch zusammenpassen, gruppieren und von Funktionen isolieren, die zu schlechtem oder unvorhersehbarem Verhalten führen können. Es kann auch dazu beitragen, die Details vor dem Endbenutzer zu verbergen, was zur Erhöhung der Sicherheit und Zuverlässigkeit beitragen kann.

Betrachten Sie diesen Beitrag von John D Cook . Bedenken Sie, dass Sie ein BreadObjekt haben. Eine natürliche Sache, um dieses Brot zu schneiden. Sie schreiben also eine slice()Funktion, damit Sie dies tun können

slice(loaf)

mit einem neuen loafObjekt, das Sie erstellt haben. Das macht Sinn. Aber wenn Sie nicht aufpassen, können Sie versehentlich anrufen

slice(finger)

mit einem fingerObjekt irgendwo in Ihrem Projekt. Dies könnte zu sehr schlechten Dingen führen. Kapselen Sie stattdessen diese Funktion / Methode in eine BreadKlasse, damit Sie dies tun können

loaf.slice()

Dies hilft definitiv dabei, ein versehentliches Aufrufen zu vermeiden finger.slice(), da fingerwahrscheinlich keine slice()Methode damit verbunden ist.

Dies ist ein erfundenes Beispiel, aber ich fand es hilfreich. Kapselung kann manchmal ein unterschätzter Aspekt von OOP sein, aber es ist ein guter.

joshin4colours
quelle
2

Durch die Kapselung können große Gruppen von Entwicklern ihre Arbeit effizienter koordinieren. Jede Entwicklergruppe arbeitet an verschiedenen Modulen, und diese Module sind gekapselt. Teilen Sie den Code in eine kleine Anzahl allgemein verfügbarer Operatoren auf, deren Verwendung sie nicht genau steuern können, und eine große Anzahl interner Operatoren, die sie genau steuern können. Dies bedeutet, dass jede Gruppe von Entwicklern Invarianten definieren kann , die für die Verwaltung ihres Moduls wichtig sind, und sicherstellen kann, dass diese Invarianten unabhängig von den Aktivitäten der Entwickler anderer Module gültig sind.

Da die Kapselung die Erhaltung von Invarianten ermöglicht, wird sie häufig zur Aufrechterhaltung von Sicherheitsinvarianten verwendet, z

  • dass auf einen Datenspeicher niemals ohne ordnungsgemäße Anmeldeinformationen zugegriffen wird
  • dass auf einem Bankkonto nie mehr Geld hinzugefügt wird, als von einem anderen entfernt wurde
  • dass bestimmte Vorgänge immer protokolliert werden

Das Objektfähigkeitsmodell ist eine Methode zum Schreiben von sicherheitsrelevantem Code, der in Steroiden gekapselt ist.

Das Sicherheitsmodell beruht darauf, dass keine Referenzen gefälscht werden können. Siehe Adressen von Akteuren zusammenfassen.

Objekte können nur durch Senden von Nachrichten zu Referenzen interagieren. Eine Referenz kann erhalten werden durch:

  1. Anfangsbedingungen: Im Anfangszustand der beschriebenen Rechenwelt kann Objekt A bereits einen Verweis auf Objekt B haben. Elternschaft: Wenn A B erstellt, erhält A in diesem Moment den einzigen Verweis auf das neu erstellte B.
  2. Begabung: Wenn A B erstellt, wird B mit der Teilmenge der Referenzen von A geboren, mit denen A es ausgestattet hat.
  3. Einführung: Wenn A Verweise auf B und C hat, kann A eine Nachricht an B senden, die einen Verweis auf C enthält. B kann diesen Verweis für die spätere Verwendung aufbewahren.

Im Objektfähigkeitsmodell werden alle Berechnungen gemäß den obigen Regeln durchgeführt.

Vorteile, die eine objektorientierte Programmierung motivieren, wie Kapselung oder Verstecken von Informationen, Modularität und Trennung von Bedenken, entsprechen Sicherheitszielen wie geringste Privilegien und Privilegientrennung bei der fähigkeitsbasierten Programmierung.

Mike Samuel
quelle
2

Wie hilft die Kapselung bei Änderungen am Code und seinen Auswirkungen?

Lassen Sie mich ein typisches und vereinfachtes Beispiel geben. Angenommen, Sie verwenden keine Kapselung: Sie haben eine Reihe von Daten und verwenden ein Array, um diese Daten zu speichern, und es gibt einen anderen Teil Ihres Programms, der dieses Array verwendet. Wenn Sie jetzt irgendwann entscheiden, dass eine verknüpfte Liste die bessere Wahl für die Speicherung Ihrer Daten ist. Was passiert, wenn Sie das Array durch eine verknüpfte Liste ersetzen? Ihr Programm wird unterbrochen, es sei denn, Sie nehmen überall Änderungen vor, um die Array-Verarbeitungslogik durch eine Verknüpfungslisten-Verarbeitungslogik zu ersetzen.

Wenn Sie jedoch OO / Encapsulation verwenden, partitionieren Sie Ihr Programm wahrscheinlich in Klassen, von denen eine die Daten speichert und die andere die Daten verwendet. In der ersten Klasse verstecken Sie Ihre Implementierung (Kapselung) und machen Ihre Dienste durch Methoden wie verfügbar

size()
remove(int index)
add(int index, Object o)
get(int index)

...

Wenn Sie in diesem zweiten Fall die Implementierung der Speicherklasse von Array zu verknüpfter Liste oder zu einer anderen Sache ändern, hat dies keine Auswirkungen auf Ihre Clients. Kein Welleneffekt.

Nazar Merza
quelle
1

Die Kapselung trägt hauptsächlich dazu bei, die Auswirkungen von Änderungen zu verringern, indem möglichst viele Implementierungsdetails für die Klasse privat gehalten werden. Indem die Schnittstelle nur auf die Mitglieder beschränkt wird, die für die Verwendung der Klasse benötigt werden, können viele Änderungen an der Implementierung vorgenommen werden, ohne dass sich dies auf Code auswirkt, der die Klasse verwendet.

Das scheint der Punkt zu sein, auf den sich der von Ihnen zitierte Text bezieht, obwohl ich ihn auch bei meiner ersten Lektüre etwas verwirrend fand.

Jonathan Wood
quelle
1

Ich denke, es ermöglicht Ihnen, die Funktionalität Ihrer Klasse zu ändern, ohne, wenn auch nicht immer, BC-Unterbrechungen einzuführen (binäre oder Verhaltenskompatibilität). Das heißt, Sie können ändern, wie Ihre Klasse "etwas" tut, ohne ändern zu müssen, wie der Endbenutzer ihm sagt, dass er "etwas" tun soll.

Das Beispiel, das Sie zum Ändern des Rückgabetyps gegeben haben, ist eine BC-Unterbrechung, da jeder, der zuvor Ihre Klasse verwendet hat, die neue Version nicht ohne erneutes Kompilieren verwenden kann. Dies sollte nur als letztes Mittel erfolgen und kann in einem professionellen Umfeld nur mit Genehmigung der Architekten und nach Einreichung der Unterlagen, Benachrichtigung der Kunden usw. erfolgen.

Außerdem haben Sie die vollständige Kontrolle über den Status Ihres Objekts.

James
quelle