Anwendungsfälle für "private" Schnittstellen?

8

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?


quelle
Die "IMumle" Art, eine Schnittstelle zu bezeichnen, ist meiner Meinung nach eine schlechte Angewohnheit
Zwei Kommentare. Erstens scheint es, als könnten wir die gesamte Diskussion auf das XML-Schema und nicht auf die Besonderheiten der Sprachschnittstellen stützen. Zweitens ist "Validierung" manchmal (aber nicht immer) am besten von der "Struktur" zu trennen.
Rwong

Antworten:

3

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.

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.

Winston Ewert
quelle
Ich möchte diejenigen, die eine API verwenden, nicht wie Gefangene behandeln, aber ich habe über etwas nachgedacht, das Richtlinien für die Strukturierung eines Plugins für ein vorhandenes Framework enthält. Die Plugin-Oberfläche sagt Ihnen nur, wie Ihre Erweiterung vom Framework verwendet wird, aber sie sagt Ihnen nichts darüber, wie sich die Plugins intern verhalten sollen.
Sie haben einen Punkt über erweiterte Verwendungen, die nicht durch einen Cookie-Cutter-Ansatz abgedeckt sind, aber Sie können auch leicht auf solche mit der öffentlichen Schnittstelle stoßen (z. B. in C # müssen / möchten Sie eine zurückgeben, IList<T>aber die Schnittstelle fordert Sie auf, zurückzukehren an IEnumerable<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.
@arotter, ich bin alle dafür, Richtlinien für den besten Weg zur internen Strukturierung von Dingen bereitzustellen. Aber ich sehe keinen Grund, eine bestimmte interne Struktur zu fordern. Der springende Punkt der Schnittstelle ist die Definition der öffentlichen Schnittstelle, nicht der Implementierung.
Winston Ewert
1

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:

Schnittstellen bestehen aus Methoden, Eigenschaften, Ereignissen, Indexern oder einer beliebigen Kombination dieser vier Elementtypen. Eine Schnittstelle darf keine Konstanten, Felder, Operatoren, Instanzkonstruktoren, Destruktoren oder Typen enthalten. Es darf keine statischen Elemente enthalten. Schnittstellenmitglieder sind automatisch öffentlich und können keine Zugriffsmodifikatoren enthalten.

http://msdn.microsoft.com/en-us/library/ms173156.aspx

Ali Jafer
quelle
Ja, die Mitglieder einer Schnittstelle sind öffentlich. Das bedeutet nicht, dass Sie kein privates Mitglied eines Schnittstellentyps haben können, das Sie nicht in Ihrer eigenen Schnittstelle verfügbar machen. Das Implementieren einer Schnittstelle ist öffentlich. Die Verwendung einer Schnittstelle kann so privat sein, wie Sie möchten. OP fragt im Wesentlichen, wie er eine Klasse für die private Verwendung bestimmter Schnittstellen deklarieren kann, um sicherzustellen, dass jede implementierende Klasse diese Methoden tatsächlich implementiert.
Marjan Venema
Ja, ich stimme Ihnen zu, weshalb ich einen Link gepostet habe, der eine Schnittstelle zu privaten Mitgliedern zeigt.
Ali Jafer
Ah, ok. Gut versteckt ... :)
Marjan Venema
Die Frage betraf nicht Schnittstellen in C # (oder einer anderen vorhandenen Sprache), sondern das grundlegende Konzept. Dieser Beitrag spricht es überhaupt nicht an.
Peter Taylor
0

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?

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.

Abhi Beckert
quelle
0

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 Freddeklariert internalist und eine Schnittstelle IFredeine Methode hat, die a zurückgibt Fred, muss die Schnittstelle deklariert werdeninternal. Wenn verschachtelte KlasseFred.Joeis privateund verschachtelte Schnittstelle Fred.IJoehat eine Methode, die a zurückgibt Fred.Joe, dann Fred.IJoemuss ebenfalls deklariert werden private.

Superkatze
quelle