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 SqlDatabase
und OracleDatabase
Instanzen, 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 database
Objekt von einem bestimmten Objekt erbt, Sie kümmern sich nur darum, dass es eine addProduct
Methode 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
.