So verwenden Sie keine Schnittstellen in C #

Beim Entwerfen einer Anwendung müssen Sie häufig Schnittstellen und abstrakte Klassen verwenden. Dieser Artikel beschreibt einige häufige Beispiele für „Schnittstellenmissbrauch“ und die Strategien, mit denen wir sie vermeiden können. Außerdem wird erläutert, was unter dem Grundsatz „Programmieren auf eine Schnittstelle und nicht auf eine Implementierung“ zu verstehen ist.

Was sind Schnittstellen?

Lassen Sie uns zunächst ein Verständnis der Schnittstellen erlangen und erklären, warum sie für die Programmierung benötigt werden. Eine Schnittstelle ist ausschließlich ein Vertrag. Es hat keine Implementierung. Eine Schnittstelle enthält nur Mitgliedsdeklarationen. Sie können Methodendeklarationen, aber keine Definitionen haben. Die in einer Schnittstelle deklarierten Mitglieder sollten in den Typen (Klassen und Strukturen) implementiert werden, die die Schnittstelle erweitern oder implementieren. Eine Schnittstelle darf keine Felder enthalten. Eine Schnittstelle kann nicht serialisiert werden, da sie keine Datenelemente haben kann. Wie gesagt, eine Schnittstelle kann nur Deklarationen und keine Definitionen haben.  

Vermeiden Sie Änderungen an den Schnittstellen 

Eine Klasse oder Struktur, die eine Schnittstelle erweitert, sollte alle ihre Mitglieder implementieren. Wenn sich die Implementierung ändert, funktioniert Ihr Code weiterhin. Wenn sich jedoch der Vertrag, dh die Schnittstelle, ändert, müssen Sie die Implementierungen aller Typen ändern, die die Schnittstelle erweitern. Mit anderen Worten, jede Änderung an der Schnittstelle wirkt sich auf alle Typen aus, die die Schnittstelle erweitern. Die Typen, die die Schnittstelle erweitern, müssen dem Vertrag entsprechen. Verwenden Sie Schnittstellen daher nur, wenn Sie sie selten ändern müssen. Außerdem ist es im Allgemeinen besser, eine neue Schnittstelle zu erstellen, als eine vorhandene zu ändern. 

Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung

Möglicherweise haben Sie ab und zu die Worte "Programmieren auf eine Schnittstelle und nicht auf eine Implementierung" gehört. Möglicherweise haben Sie Schnittstellen in Ihrem Code verwendet, aber Sie haben immer noch für die Implementierung programmiert. Lassen Sie uns nun den Unterschied zwischen den beiden Ansätzen untersuchen.

Wenn Sie auf eine Schnittstelle programmieren, verwenden Sie anstelle einer konkreten Implementierung die allgemeinste Abstraktion (eine Schnittstelle oder eine abstrakte Klasse). Da Schnittstellen Einheitlichkeit gewährleisten, bedeutet die Programmierung auf eine Schnittstelle, dass Sie ähnliche Objekte einheitlich behandeln können. Auf diese Weise werden Sie von der Implementierung entkoppelt, dh Ihre Implementierungen können variieren. Dies erhöht auch die Flexibilität Ihrer Designs.

Das folgende Codefragment veranschaulicht die Programmierung auf eine Schnittstelle. Stellen Sie sich eine Schnittstelle mit dem Namen IRepository vor, die die Deklaration einiger Methoden enthält. Die Klassen ProductRepository und CustomerRepository erweitern die IRepository-Schnittstelle und implementieren die in der IRepository-Schnittstelle deklarierten Methoden, wie unten gezeigt.

öffentliche Schnittstelle IRepository

    {

        // Code

    }}

    public class ProductRepository: IRepository

    {

        // Code

    }}

    public class CustomerRepository: IRepository

    {

        // Code

    }}

Der folgende Code kann verwendet werden, um eine Instanz des ProductRepository zu erstellen.

IRepository Repository = neues ProductRepository ();

Die Idee ist, dass Sie hier jede Klasse verwenden können, die die IRepository-Schnittstelle implementiert. Die folgende Aussage ist also ebenfalls gültig.

IRepository Repository = neues CustomerRepository ();

Wenn Sie auf eine Implementierung programmieren, geht diese Einheitlichkeit verloren. Stattdessen verfügen Sie normalerweise über einige Konstrukte, z. B. die Anweisungen "if..else" oder "switch..case", um das Verhalten in Ihrem Code zu steuern.

Vermeiden Sie eine Überbeanspruchung der Schnittstellen

Es ist keine gute Praxis, jede Klasse einer Schnittstelle zuzuordnen. Eine übermäßige Verwendung von Schnittstellen auf diese Weise führt zu unnötiger Komplexität, führt zu Redundanz des Codes, verletzt YAGNI und verringert die Lesbarkeit und Wartbarkeit der Codebasis. Schnittstellen werden verwendet, um Objekte mit identischem Verhalten zu gruppieren. Wenn sich die Objekte nicht identisch verhalten, ist diese Gruppierung nicht erforderlich. Die Verwendung von Schnittstellen, wenn Sie nicht mehrere Implementierungen davon haben, ist ein Beispiel für eine Überbeanspruchung der Schnittstelle.

Das Erstellen einer Schnittstelle für eine Klasse, die den öffentlichen Mitgliedern der Klasse entspricht, ist weit verbreitet. Auf diese Weise fügen Sie überhaupt keinen Wert hinzu - Sie duplizieren lediglich die Schnittstelle der Klasse, ohne eine echte Abstraktion hinzuzufügen.

Schauen wir uns nun ein Beispiel an, wie Schnittstellen überbeansprucht werden. Betrachten Sie die folgende Schnittstelle mit dem Namen IProduct.

öffentliche Schnittstelle IProduct

    {

        int Id {get; einstellen; }}

        Zeichenfolge ProductName {get; einstellen; }}

        doppelter Preis {get; einstellen; }}

        int Menge {get; einstellen; }}

    }}

Die Produktklasse erweitert die IProduct-Schnittstelle wie unten gezeigt.

öffentliche Klasse Produkt: IProduct

    {

        public int Id {get; einstellen; }}

        öffentliche Zeichenfolge ProductName {get; einstellen; }}

        öffentlicher Doppelpreis {get; einstellen; }}

        public int Quantity {get; einstellen; }}

    }}

Natürlich benötigen wir die IProduct-Schnittstelle nicht, da die Schnittstelle und ihre Implementierung identisch sind. Der redundante Code ist nicht erforderlich.

Schauen wir uns ein anderes Beispiel an. Das folgende Codeausschnitt zeigt eine Schnittstelle mit dem Namen IProductManager mit der Deklaration von zwei Methoden, nämlich Speichern und Aktualisieren.

 öffentliche Schnittstelle IProductManager

    {

        void Save (IProduct-Produkt);

        void Update (IProduct-Produkt);

    }}

Die IProductManager-Schnittstelle enthält die Deklarationen der öffentlichen Methoden der ProductManager-Klasse. So sieht die ProductManager-Klasse aus.

 public class ProductManager: IProductManager

    {

        public void Save (IProduct-Produkt)

        {

           // Schreiben Sie hier Ihre Implementierung

        }}

        public void Update (IProduct-Produkt)

        {

            // Schreiben Sie hier Ihre Implementierung

        }}

    }}

Die Schnittstellen IProduct und IProductManager sind Beispiele für eine Überbeanspruchung der Schnittstelle. Beide Schnittstellen haben eine einzige Implementierung und bieten keinerlei Mehrwert.

Mithilfe von Schnittstellen können Sie unnötige Kopplungen in Ihrem Code entfernen und Ihren Code leicht testbar machen. Eine übermäßige Verwendung von Schnittstellen sollte jedoch vermieden werden. Verwenden Sie Schnittstellen nur, wenn mehr als eine Implementierung vorhanden ist. Sie können Schnittstellen auch verwenden, wenn Sie eine Klasse haben, die viele Rollen spielt oder mehrere Verantwortlichkeiten hat. In diesem Fall kann Ihre Klasse mehrere Schnittstellen implementieren - eine für jede Rolle.