Ich habe mich gefragt, ob es einen gültigen Anwendungsfall gibt, um die spezifischen internen Eigenschaften und Funktionen einer Klasse auf ähnliche Weise wie eine Schnittstelle die öffentlichen Eigenschaften und Funktionen einer Klasse definieren zu können.
Stellen Sie sich die Aufgabe vor, eine Klasse aufzubauen, die einen Menschen beschreibt.
Offensichtlich ist jeder Mensch eine humanoide Kreatur, aber nicht jede humanoide Kreatur ist ein Mensch. Sie hätten also wahrscheinlich eine Schnittstelle IHumanoid mit Funktionen wie diesen (da es nicht sinnvoll ist, den Körperplan in der Klasse fest zu codieren):
public interface IHumanoid {
function get head():IHead;
function get torso():ITorso;
function get leftArm():IArm;
function get rightArm():IArm;
function get leftLeg():ILeg;
function get rightLeg():ILeg;
}
Weiter und offensichtlich ist jeder Mensch ein Säugetier, aber nicht jedes Säugetier ist ein Mensch, daher gibt es wahrscheinlich eine andere IMammal-Schnittstelle mit zwei Definitionen für Männer und Frauen, die irgendwo herumschweben:
public interface IMammal {
function procreate(partner:IMammal):void;
}
public interface IMaleMammal extends IMammal {
function inseminate(female:IFemaleMammal):void;
}
public interface IFemaleMammal extends IMammal {
function conceive(partner:IMaleMammal):Boolean;
function giveBirth():IMammal;
function nurse(offspring:IMammal):void;
}
So sieht unsere Klasse jetzt wahrscheinlich ungefähr so aus:
public class Human implements IHumanoid, IMammal {
private var _head:IHead;
private var _torso:ITorso;
private var _leftArm:IArm;
private var _rightArm:IArm;
private var _leftLeg:ILeg;
private var _rightLeg:ILeg;
public function Human() {
// ctor...
}
public function get head():IHead {
return _head;
}
public function get torso():ITorso {
return _torso;
}
public function get leftArm():IArm {
return _leftArm;
}
public function get rightArm():IArm {
return _rightArm;
}
public function get leftLeg():ILeg {
return _leftLeg;
}
public function get rightLeg():ILeg {
return _rightLeg;
}
public function procreate(partner:IMammal):void {
// "abstract" function
}
}
public class MaleHuman extends Human implements IMaleMammal {
override public function procreate(partner:IMammal):void {
if (partner is IFemaleMammal) {
inseminate(partner);
}
}
public function inseminate(female:IFemaleMammal):void {
female.conceive(this);
}
}
public class FemaleHuman extends Human implements IFemaleMammal {
override public function procreate(partner:IMammal):void {
if (partner is IMaleMammal) {
conceive(partner);
}
}
public function conceive(partner:IMaleMammal):Boolean {
// ...
}
public function giveBirth():IMammal {
// ...
}
public function nurse(offspring:IMammal):void {
// ...
}
}
Von hier aus können wir unsere Klassen weiter implementieren und alles funktioniert gut und gut, bis wir die Aufgabe bekommen, die vorhandenen Schnittstellen zu verwenden, um einige andere Klassen zu implementieren. Vielleicht ein Gorilla, ein Orca und ein Schnabeltier.
Wenn wir das massive Problem ignorieren, das das Schnabeltier für unsere aktuelle Schnittstellenstruktur darstellt (* Husten * Eiablagesäugetier * Husten *), haben wir das "Problem", dass uns nichts daran hindert, dem Gorilla 2 Gehirn, der Orca 8 Lunge und der Schnabeltierhälfte zu geben ein Dutzend Lebern. Und obwohl wir diszipliniert genug sind, um der Struktur zu folgen, die Säugetiere normalerweise haben, können wir dies nicht garantieren, wenn wir die API für andere Entwickler öffnen, die möglicherweise einige ernsthaft vermasselte Dinge codieren, die für die Außenwelt immer noch in Ordnung aussehen.
Daher habe ich mich gefragt, ob es einen gültigen Anwendungsfall gibt, um so etwas wie eine "private Schnittstelle" zu erstellen, die nicht öffentliche Funktionen und Eigenschaften definiert. Vielleicht etwas in diese Richtung:
public structure SMammal {
function get brain():IBrain;
function get liver():ILiver;
function get leftLung():ILung;
function get rightLung():ILung;
function get leftKidney():IKidney;
function get rightKidney():IKidney;
}
public class Human implements IHumanoid, IMammal follows SMammal {
private function get brain():IBrain {
// ...
}
private function get liver():ILiver {
// ...
}
// etc. etc.
}
Gibt es eine solche Funktion in einer Programmiersprache? Können abstrakte Klassen verwendet werden, um dies zu lösen? Oder sollten wir uns überhaupt nicht darum kümmern, solange die öffentliche Schnittstelle irgendwie wie erwartet funktioniert?
Antworten:
Was genau gewinnen Sie, wenn Sie sie zwingen, Ihrer Struktur zu folgen? Sie brechen möglicherweise einige fortgeschrittene Verwendungen, vielleicht gibt es einen Fall für ein Tier mit mehreren Gehirnen. Und der Client-Programmierer kann die Implementierung einer Klasse auf so viele verrückte Arten durcheinander bringen, dass der Versuch, dies zu verhindern, sich anfühlt, als würde er versuchen, ein Fenster zu schließen, um ein Pferd in einer Scheune zu halten.
Behandeln Sie Ihre Kundenprogrammierer nicht wie Gefangene. Wenn sie der öffentlichen Schnittstelle folgen, lassen Sie den Code laufen.
quelle
IList<T>
aber die Schnittstelle fordert Sie auf, zurückzukehren anIEnumerable<T>
). Und last but not least gibt es afaik kein bekanntes Tier mit mehreren (redundanten, wie alle unsere duplizierten Organe) Gehirnen. Während es also einen Fall für so etwas gibt (z. B. Spinnen), wird es mit ziemlicher Sicherheit kein Säugetier sein, also wäre es sowieso eine völlig andere Schnittstelle / Struktur.Da ein Schnittstellentyp standardmäßig ein öffentliches Mitglied ist, sollte alles darin öffentlich sein, aber ich habe Folgendes gesehen:
/programming/792908/what-is-a-private-interface
von msdn:
http://msdn.microsoft.com/en-us/library/ms173156.aspx
quelle
Das tut es sicher. In einigen Sprachen gibt es so ziemlich jede Funktion.
In Objective-C ++ (iOS / Mac-Programmierung) ist es beispielsweise Standardverfahren für eine Klasse, mindestens zwei Schnittstellen zu haben. Darauf ist öffentlich und auf privat. Manchmal verfügen sie auch über zusätzliche Schnittstellen, die an anderer Stelle definiert sind (z. B. verfügt die Zeichenfolgenklasse auf niedriger Ebene über eine Schnittstelle zum Ausführen von GUI-Bildschirmzeichnungsvorgängen für die Zeichenfolge, die in einem vollständig von der Definition definierten Framework / einer Bibliothek definiert ist Klasse).
Grundsätzlich ist die Art und Weise, wie ich öffentlich / privat behandle, einfach:
Öffentliche Schnittstellenelemente können niemals geändert werden. Wann immer Sie Ihren Code umgestalten oder verbessern, muss sich die öffentliche Schnittstelle immer noch genauso verhalten wie zuvor.
Private Schnittstellen hingegen können jederzeit geändert oder komplett entfernt werden. Solange Sie den gesamten Code in der Klasse aktualisieren, um das neue Verhalten zu verstehen, ist alles in Ordnung. Oft ist die öffentliche Schnittstelle voll von ein- oder zweizeiligen Methoden, die die eigentliche Arbeit an die private Schnittstelle übergeben.
Objective-C ++ hat auch das Konzept "geschützt" in der Schnittstelle, wo etwas für Unterklassen verfügbar ist, aber nicht für externe Klassen.
Normalerweise wird mein Code ungefähr zur Hälfte zwischen öffentlich und privat aufgeteilt. Ich benutze nicht viel geschützt, obwohl es hin und wieder nützlich ist.
quelle
Zumindest in .NET-Sprachen ist es möglich, einen Zugriffsspezifizierer auf eine Schnittstelle anzuwenden. Dies kann hilfreich sein, wenn einige Kombinationen der internen Klassen einer Assembly gemeinsame Fähigkeiten haben, diese Kombinationen und Fähigkeiten jedoch nicht zu einer hierarchischen Beziehung passen. Die Tatsache, dass die Fähigkeiten nicht zu einer hierarchischen Beziehung passen, macht es erforderlich, Schnittstellen zu verwenden, um sie darzustellen, impliziert jedoch in keiner Weise, dass externer Code Typen definieren darf, die behaupten, die Schnittstellen zu implementieren. Außerdem dürfen Schnittstellen keine Methoden enthalten, deren Parametertypen oder Rückgabetypen einen engeren Zugriff haben als die Schnittstellen selbst. Wenn eine Klasse
Fred
deklariertinternal
ist und eine SchnittstelleIFred
eine Methode hat, die a zurückgibtFred
, muss die Schnittstelle deklariert werdeninternal
. Wenn verschachtelte KlasseFred.Joe
isprivate
und verschachtelte SchnittstelleFred.IJoe
hat eine Methode, die a zurückgibtFred.Joe
, dannFred.IJoe
muss ebenfalls deklariert werdenprivate
.quelle