Cette réponse explique très bien les différences entre une classe abstraite et une interface, mais ne dit pas pourquoi vous devriez en déclarer une.
D'un point de vue purement technique, il n'est jamais nécessaire de déclarer une classe abstraite.
Considérez les trois classes suivantes:
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
Vous n'avez pas besoin de rendre la classe Database abstraite, même si son implémentation pose un problème évident: lorsque vous écrivez ce programme, vous pouvez taper new Database()
et il serait valide, mais cela ne fonctionnerait jamais.
Quoi qu'il en soit, vous obtiendriez toujours un polymorphisme. Ainsi, tant que votre programme ne fabrique SqlDatabase
que des OracleDatabase
instances, vous pouvez écrire des méthodes telles que:
public void printTableNames(Database database) {
String[] names = database.getTableNames();
}
Les classes abstraites améliorent la situation en empêchant un développeur d'instancier la classe de base, car un développeur l'a marquée avec une fonctionnalité manquante . Il offre également une sécurité au moment de la compilation, vous permettant ainsi de vous assurer que toutes les classes qui étendent votre classe abstraite fournissent la fonctionnalité minimale nécessaire pour fonctionner, sans avoir à vous soucier de la possibilité d'utiliser des méthodes de stub (comme celle ci-dessus) que les héritiers ont en quelque sorte. comme par magie, ils doivent redéfinir une méthode pour la faire fonctionner.
Les interfaces sont un sujet totalement séparé. Une interface vous permet de décrire les opérations pouvant être effectuées sur un objet. Vous utiliserez généralement des interfaces lors de l'écriture de méthodes, de composants, etc., qui utilisent les services d'autres composants, d'objets, mais vous ne vous souciez pas du type d'objet à partir duquel vous obtenez les services.
Considérez la méthode suivante:
public void saveToDatabase(IProductDatabase database) {
database.addProduct(this.getName(), this.getPrice());
}
Vous ne vous souciez pas de savoir si l' database
objet hérite d'un objet particulier, vous vous souciez juste qu'il ait une addProduct
méthode. Ainsi, dans ce cas, une interface convient mieux que de faire en sorte que toutes vos classes héritent de la même classe de base.
Parfois, la combinaison des deux fonctionne très bien. Par exemple:
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
}
}
Notez que certaines des bases de données héritent de RemoteDatabase pour partager certaines fonctionnalités (comme se connecter avant d'écrire une ligne), mais FileDatabase est une classe distincte à implémenter uniquement IProductDatabase
.