Warum sollte ich eine Klasse als abstrakte Klasse deklarieren?


40

Ich kenne die Syntax, die Regeln für abstrakte Klassen und möchte die Verwendung einer abstrakten Klasse kennen

Abstrakte Klassen können nicht direkt instanziiert, sondern durch andere Klassen erweitert werden

Was ist der Vorteil davon?

Wie unterscheidet es sich von einem Interface?

Ich weiß, dass eine Klasse mehrere Schnittstellen implementieren kann, aber nur eine abstrakte Klasse erweitern kann. Ist das der einzige Unterschied zwischen einer Schnittstelle und einer abstrakten Klasse?

Mir ist die Verwendung eines Interfaces bekannt. Das habe ich aus dem Event Delegation Model von AWT in Java gelernt.

In welchen Situationen sollte ich Klasse als abstrakte Klasse deklarieren? Was ist der Nutzen davon?


14
Dies wurde gefragt, wissen Sie. Eine Suche wird andere Fragen wie diese auftauchen lassen. Sie sollten auf Google starten, was Sie zum Stapelüberlauf führt. All dies sind Duplikate: stackoverflow.com/search?q=abstract+interface .
S.Lott

1
"Verwendung der abstrakten Klasse, die sie gerade besprechen ... Regeln". Was? Was ist der Unterschied zwischen "Regeln" und "Verwendung"? Ihre genaue Frage wurde übrigens gestellt. Ich weiß. Ich habe geantwortet. Weiter suchen. Es ist wichtig zu lernen, wie man die Suche verwendet.
S.Lott

Ich meine "In welchen Situationen sollte ich Klasse als abstrakte Klasse deklarieren? Was sind die Vorteile davon?"
Vaibhav Jani

Eine Schnittstelle ist eine reine abstrakte Klasse, das ist alles. Sie sind ein und dasselbe. Ich verwende die abstrakte Klasse die ganze Zeit für eine Funktion in der Basisklasse, die nicht implementiert werden kann, weil sie Daten benötigt, die nur die Unterklassen haben, aber ich möchte sicherstellen, dass jede Unterklasse diese Funktion hat und sie dementsprechend implementiert.
AngryBird

Ich finde das Template-Methodenmuster sehr leistungsfähig und ein guter Anwendungsfall für abstrakte Klassen.
m3th0dman

Antworten:


49

Mit dieser Antwort lassen sich die Unterschiede zwischen einer abstrakten Klasse und einer Schnittstelle gut erklären , aber sie beantwortet nicht, warum Sie eine deklarieren sollten.

Aus rein technischer Sicht ist es niemals erforderlich , eine Klasse als abstrakt zu deklarieren.

Betrachten Sie die folgenden drei Klassen:

class Database { 
    public String[] getTableNames() { return null; } //or throw an exception? who knows...
}

class SqlDatabase extends Database { } //TODO: override getTableNames

class OracleDatabase extends Database { }  //TODO: override getTableNames

Sie müssen nicht haben , um die Datenbank - Klasse abstrakt zu machen, auch wenn es ein offensichtliches Problem mit seiner Implementierung ist: Wenn Sie dieses Programm schreiben, könnten Sie geben new Database()und es wäre gültig, aber es würde nie funktionieren.

Unabhängig davon, würden Sie immer noch Polymorphismus bekommen, so lange Ihr Programm nur macht SqlDatabaseund OracleDatabaseInstanzen, könnten Sie Methoden schreiben wie:

public void printTableNames(Database database) {
    String[] names = database.getTableNames();
}

Abstrakte Klassen verbessern die Situation, indem sie verhindern, dass ein Entwickler die Basisklasse instanziiert, da ein Entwickler sie als fehlend markiert hat . Es bietet auch Sicherheit beim Kompilieren, sodass Sie sicherstellen können, dass alle Klassen, die Ihre abstrakte Klasse erweitern, die Mindestfunktionalität für die Arbeit bereitstellen, und dass Sie sich nicht darum kümmern müssen, Stub-Methoden (wie die oben genannten) zu platzieren, über die Erben verfügen auf magische Weise zu wissen , dass sie haben eine Methode , um außer Kraft zu setzen , damit es funktioniert zu machen.

Schnittstellen sind ein völlig eigenständiges Thema. Über eine Schnittstelle können Sie beschreiben, welche Vorgänge an einem Objekt ausgeführt werden können. Normalerweise verwenden Sie Schnittstellen, wenn Sie Methoden, Komponenten usw. schreiben, die die Dienste anderer Komponenten oder Objekte verwenden, aber es ist Ihnen egal, von welchem ​​Objekttyp Sie die Dienste erhalten.

Betrachten Sie die folgende Methode:

public void saveToDatabase(IProductDatabase database) {
     database.addProduct(this.getName(), this.getPrice());
}

Sie kümmern sich nicht darum, ob das databaseObjekt von einem bestimmten Objekt erbt, Sie kümmern sich nur darum, dass es eine addProductMethode hat. In diesem Fall ist eine Schnittstelle besser geeignet, als alle Klassen von derselben Basisklasse erben zu lassen.

Manchmal funktioniert die Kombination der beiden sehr gut. Zum Beispiel:

abstract class RemoteDatabase implements IProductDatabase { 
    public abstract String[] connect();
    public abstract void writeRow(string col1, string col2);

    public void addProduct(String name, Double price) {
        connect();
        writeRow(name, price.toString());
    }
}

class SqlDatabase extends RemoteDatabase {
    //TODO override connect and writeRow
}

class OracleDatabase extends RemoteDatabase { 
    //TODO override connect and writeRow
}

class FileDatabase implements IProductDatabase {
    public void addProduct(String name, Double price) {
         //TODO: just write to file
    }
}

Beachten Sie, wie einige Datenbanken von RemoteDatabase erben, um bestimmte Funktionen gemeinsam zu nutzen (z. B. das Herstellen einer Verbindung vor dem Schreiben einer Zeile). FileDatabase ist jedoch eine separate Klasse, die nur implementiert wird IProductDatabase.


16

Ähnlichkeiten

Für die Abstraktion werden abstrakte Klassen und Interfaces benötigt. Sie können nicht mit einem neuen instanziiert werden , sondern können bei der Inversion von Kontrollcontainern oder über Factory-Patterns aufgelöst werden.

Unterschied

  1. Schnittstellen

    • Definieren Sie bekannte öffentliche Aufträge, Fähigkeiten des Typs
    • Anwendbar, um horizontale Vererbung anzuzeigen, dh Verzweigung auf der ersten Vererbungsebene (z. B. ILog, um Protokollierungsfunktionen für Datenbank, Textdatei, XML, SOAP usw. zu definieren)
    • Alle Mitglieder sind öffentlich
    • Keine Implementierung erlaubt
    • Vererbungskind kann viele zu implementierende Schnittstellen haben
    • Nützlich für die Integration von Drittanbietern
    • Die Benennung beginnt normalerweise mit I
  2. Abstrakte Klasse

    • Definieren Sie die Struktur, die Identität und einige standardmäßig unterstützte Verhaltensweisen
    • Anwendbar, um vertikale Vererbung zu zeigen, dh tiefe Verzweigung auf den verschiedenen Ebenen (z. B. AbstractEntity-Klasse in domänengetriebener Entwicklung)
    • Mitglieder können unterschiedliche Sichtbarkeiten haben (von öffentlich bis privat)
    • Sie können einige Mitglieder implementieren (zB * Reader-Klassen)
    • Vererbungskind kann nur eine abstrakte Basisklasse haben

Es ist eigentlich einfach, die Antwort durch einfache Google-Abfrage zu finden .


was darf man durch umsetzung nicht mena? Java-Klasse kann Schnittstellen implementieren.
Sajuuk

@Sajuuk diese Zeile bezieht sich auf eine Schnittstelle. Sie können die Implementierung eines Vertrags nicht in eine Schnittstelle einfügen. Möglicherweise haben Sie eine Standardimplementierung eines Vertrags in einer abstrakten Klasse.
oleksii

11

Wie unterscheidet es sich von einem Interface?

In einer abstrakten Klasse können Sie einige Methoden implementieren und den Rest von der Erweiterungsklasse implementieren lassen (erzwingen). Sie können keine Methoden in einer Schnittstelle implementieren. Sie können niemanden zwingen, etwas außer Kraft zu setzen, wenn Sie eine gewöhnliche Klasse erweitern. Mit einer abstrakten Klasse können Sie.


Hierfür ist ein Update für Java 8 erforderlich, für das Standardmethoden eingeführt wurden.
Haakon Løtveit

8

Abstrakte Klassen sind für "ist ein" Beziehungen und Schnittstellen sind für "können".

Mit abstrakten Klassen können Sie Basisverhalten hinzufügen, damit Programmierer nicht alles codieren müssen, während sie dennoch gezwungen sind, Ihrem Entwurf zu folgen.


3

Neben den tiefen technischen Details - wie die Implementierung einiger Methoden für abstrakte Klassen usw. - lautet die Bedeutung wie folgt:

Schnittstellen definieren gemeinsame Fähigkeiten - IEnumerable definiert, dass die Klasse, die diese Schnittstelle implementiert, aufgelistet werden kann. Es sagt nichts über die Klasse selbst aus.

Abstrakte (oder Basis-) Klassen definieren das Verhalten - WebRequest definiert ein gemeinsames Verhalten aller untergeordneten Klassen wie HttpWebRequest usw. Es definiert die Kernbedeutung der Klasse und ihren eigentlichen Zweck - den Zugriff auf Webressourcen.


2

Wikipedia-Eintrag .

Der Hauptunterschied zwischen einer Schnittstelle und einer abstrakten Klasse besteht darin, dass eine abstrakte Klasse möglicherweise implementierte Methoden bereitstellt. Mit Interfaces können Sie nur Methoden deklarieren , deren Signatur schreiben. Hier ist ein Beispiel für eine Klasse, die eine abstrakte Klasse erweitert, die zwei Schnittstellen implementiert: (Java)

interface MyInterface1 {
  string getValue1();
}

interface MyInterface2 {
  string getValue2();
}

abstract class MyAbstractClass implements MyInterface1, MyInterface2{
  void printValues() {
    System.out.println("Value 1: " + getValue1() + ", Value 2: " + getValue2() + 
                       ", Value 3: " + getValue3());
  }

  protected abstract string getValue3();
}

class ImpClass extends MyAbstractClass {
  public string getValue1() {
    return "1";
  }

  public string getValue2() {
    return "2";
  }

  protected string getValue3() {
    return "3";
  }
}

In diesem Beispiel stellt die MyAbstractClass eine öffentliche Methode bereit, die alle drei Werte ausgibt . In ImpClass müssen Sie getValue1 und getValue2 aus MyInterface1 bzw. MyInterface2 und getValue3 aus der abstrakten Klasse implementieren .

Voilà.

Es gibt weitere Aspekte (Schnittstelle: nur öffentliche Methoden, abstrakte Klasse: geschützte abstrakte und öffentliche abstrakte Methoden), die Sie jedoch selbst lesen können.

Abschließend ist eine abstrakte Klasse, die nur abstrakte Methoden bereitstellt, eine "reine" abstrakte Basisklasse, auch bekannt als Schnittstelle.


2
  • Schnittstelle - wenn einige Klassen eine API gemeinsam nutzen (Methodennamen und Parameter)
  • Abstrakte Klasse - wenn einige Klassen denselben Code verwenden (Implementierung)

Mit anderen Worten, Sie sollten mit der Frage beginnen: "Teilen sich diese Klassen notwendigerweise die Implementierung oder haben sie nur eine gemeinsame Schnittstelle ?"

Wenn die Antwort gemischt ist, wie z. B. - diese drei Klassen müssen die Implementierung gemeinsam nutzen, aber diese beiden anderen teilen nur ihre API -, können Sie für alle fünf eine Schnittstelle und für diese drei Klassen eine abstrakte Klasse mit der gemeinsamen Schnittstelle erstellen Code.

Es gibt auch andere Möglichkeiten, die Implementierung gemeinsam zu nutzen, z. B. um ein Objekt mit dieser Implementierung zu kapseln (z. B. im Strategiemuster ).


1

Sie würden ein Class Abstract deklarieren, wenn Sie nicht möchten, dass der Entwickler (wahrscheinlich Sie selbst) es instanziiert, weil es nicht funktioniert oder keinen Sinn ergibt.

Stellen Sie sich zum Beispiel ein Spiel vor, in dem es verschiedene Arten von Spieleinheiten gibt. Sie alle erben von der Basisklasse GameEntity.

abstract class GameEntity{

    int lifePoint, speed, damage;

    public attack(GameEntity target){ target.damage(damage); }

    public damage(int damageInflicted){ lifePoint -= damageInflicted - speed; }

    // etc...

}

Diese Klasse wird deklariert, abstractda es keinen Sinn macht, sie zu instanziieren. Es deklariert einige Aktionen für die Spielelemente und einige Attribute, aber nirgendwo in dieser Klasse werden diese Attribute initialisiert. Diese Klasse dient als Vorlage für die Spielelemente, ist jedoch nicht dazu gedacht, eigenständig instanziiert und als solche deklariert zu werden abstract.

In Bezug auf den Unterschied in der Verwendung zwischen einer abstrakten Klasse und einer Schnittstelle:

Aus meiner Sicht ist eine Schnittstelle eine Möglichkeit, polymorphes Verhalten zu erreichen, ohne durch den Mechanismus der einmaligen Vererbung einiger Sprachen eingeschränkt zu sein.

Kehren wir als Beispiel zum Spiel zurück. Betrachten Sie eine Klasse, Enemydie von abgeleitet ist GameEntity. Diese Klasse hat eine Methode attackMeFromDistance(RangedAttacker attacker). Diese Methode soll es Einheiten ermöglichen, den Feind von weitem anzugreifen.

Wie Sie sehen, nimmt diese Methode einen RangedAttackerTyp als Parameter an. Alle Spieleinheiten erben jedoch bereits von GameEntity. Sie können keine weitere Klasse erweitern.

Nehmen Sie den Unterricht Mageund Archerzum Beispiel. Wir möchten, dass beide als Parameter in der attackMeFromDistance(RangedAttacker attacker)Methode akzeptiert werden, aber sie sind bereits von abgeleitet GameEntity.

Um dies zu lösen, erstellen wir eine neue Schnittstelle:

interface RangedAttacker{
    public void attackFromDistance();
}

Eine Klasse, die diese Schnittstelle implementiert, muss die attackFromDistance()Methode implementieren. Auf diese Weise wird sichergestellt, dass sie über verschiedene Angriffsmöglichkeiten verfügt. Dies bedeutet, dass die attackMeFromDistanceMethode jetzt sicher Klassen akzeptieren kann, die diese Schnittstelle implementieren. Das Erstellen Mageund ArcherImplementieren dieser Schnittstelle löst unser Problem.

Für mich ist das die Kraft der Schnittstellen.

Zusammenfassend würde man normalerweise eine abstrakte Klasse verwenden, wenn man für einige Klassen eine Basisklasse haben möchte, aber es wäre nicht sinnvoll, sie selbst zu instanziieren (oder in einem Fall, in dem es abstractMethoden gibt, die es sein müssen) implementiert von den Unterklassen, und in diesem Fall würde der Compiler Sie zwingen, die Klasse zu machen abstract). Sie würden eine Schnittstelle verwenden, um polymorphes Verhalten zu erzielen, ohne durch den Mechanismus der einmaligen Vererbung eingeschränkt zu sein.


0
  1. Es besteht die Möglichkeit, dass einige Klassen zu wenige Methoden gemeinsam haben (Geschäftslogik). Und die übrigen Methoden sind unterschiedlich. In solchen Szenarien können Sie alle gängigen Methoden in einer Klasse implementieren und die verbleibenden als abstrakt deklarieren. Dann sollten Sie die Klasse als abstrakt deklarieren.
  2. Manchmal können Sie das Objekt nicht direkt für die Klasse erstellen. Diese Art von Klasse müssen Sie als abstrakt deklarieren.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.