Je connais la syntaxe, les règles appliquées à la classe abstraite et je veux connaître l'utilisation d'une classe abstraite
La classe abstraite ne peut pas être instanciée directement mais peut être étendue par une autre classe
Quel est l'avantage de le faire?
En quoi est-ce différent d'une interface?
Je sais qu'une classe peut implémenter plusieurs interfaces mais ne peut étendre qu'une seule classe abstraite. Est-ce la seule différence entre une interface et une classe abstraite?
Je connais l'utilisation d'une interface. J'ai appris cela du modèle de délégation d'événement d'AWT en Java.
Dans quelles situations devrais-je déclarer la classe comme une classe abstraite? Quels sont les avantages de cela?
This La réponse explique bien les différences entre une classe abstraite et une interface, mais ce n'est pas le cas répondez pourquoi vous devez en déclarer un.
D'un point de vue purement technique, il n'y a jamais exigence pour déclarer une classe comme 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 pour rendre la classe Database abstraite, même s'il y a un problème évident avec son implémentation: Lorsque vous écrivez ce programme, vous pourriez tapez new Database()
et ce serait valide, mais cela ne fonctionnerait jamais.
Quoi qu'il en soit, vous obtiendrez toujours un polymorphisme, donc tant que votre programme ne crée que des instances SqlDatabase
et OracleDatabase
, vous pouvez écrire des méthodes comme:
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 comme ayant fonctionnalité manquante . Il fournit également une sécurité au moment de la compilation afin que vous puissiez vous assurer que toutes les classes qui étendent votre classe abstraite fournissent le minimum de fonctionnalités pour fonctionner, et vous ne ' t besoin de s'inquiéter de mettre des méthodes de stub (comme celle ci-dessus) que les héritiers doivent en quelque sorte savoir par magie qu'ils ont pour remplacer une méthode afin de faire ça marche.
Les interfaces sont un sujet totalement distinct. Une interface vous permet de décrire quelles opérations peuvent ê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, objets, mais vous ne vous souciez pas du type réel d'objet dont 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'objet database
hérite d'un objet particulier, vous vous souciez simplement qu'il possède une méthode addProduct
. Donc, dans ce cas, une interface est mieux adaptée 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
}
}
Remarquez comment certaines des bases de données héritent de RemoteDatabase pour partager certaines fonctionnalités (comme la connexion avant d'écrire une ligne), mais FileDatabase est une classe distincte qui implémente uniquement IProductDatabase
.
Similitudes
Des classes abstraites et des interfaces sont requises pour l'abstraction. Ils ne peuvent pas être instanciés avec un nouveau , mais peuvent être résolus en inversion des conteneurs de contrôle ou via des modèles d'usine.
Différence
Interfaces
Classe abstraite
Il est en fait facile de trouver la réponse par simple requête google .
En quoi est-ce différent d'une interface?
Dans une classe abstraite, vous pouvez implémenter certaines méthodes et laisser (forcer) le reste à être implémenté par la classe d'extension. Vous ne pouvez pas implémenter de méthodes dans une interface. Vous ne pouvez forcer personne à remplacer quoi que ce soit lors de l'extension d'une classe ordinaire. Avec une classe abstraite, vous pouvez.
Les classes abstraites sont pour les relations "est un" et les interfaces sont pour "peut faire".
Les classes abstraites vous permettent d'ajouter un comportement de base afin que les programmeurs n'aient pas à tout coder, tout en les forçant à suivre votre conception.
Outre les détails techniques profonds - comme la mise en œuvre de certaines méthodes pour les classes abstraites, etc., la signification est la suivante:
Les interfaces définissent une capacité commune - IEnumerable définit que la classe qui implémente cette interface peut être énumérée. Il ne dit rien sur la classe elle-même.
Les classes abstraites (ou de base) définissent le comportement - WebRequest définit un comportement commun à toutes les classes enfants comme HttpWebRequest, etc. Il définit la signification principale de la classe et son objectif réel - d'accéder aux ressources Web.
La principale différence entre une interface et une classe abstraite est qu'une classe abstraite peut fournir des méthodes implémentées. Avec les interfaces, vous ne pouvez que déclarer les méthodes, écrire leur signature. Voici un exemple de classe qui étend une classe abstraite qui implémente deux interfaces: (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";
}
}
Dans cet exemple, le MyAbstractClass fournit une méthode publique qui imprime les trois valeurs. Dans ImpClass , vous devez implémenter getValue1 et getValue2 respectivement à partir de MyInterface1 et MyInterface2 = et getValue3 de la classe abstraite.
Voilà.
Il y a plus d'aspects (interface: uniquement les méthodes publiques, classe abstraite: méthodes abstraites protégées et méthodes abstraites publiques) mais vous pouvez le lire par vous-même.
Sur une note finale, une classe abstraite qui ne fournit que des méthodes abstraites est une classe de base abstraite "pure", alias, une interface.
En d'autres termes, vous devriez commencer par une question: "ces classes partagent-elles nécessairement l'implémentation , ou ont-elles simplement une commune interface ? "
Si la réponse est mitigée, par exemple - ces trois classes doivent partager l'implémentation, mais ces deux autres ne partagent que leur API - alors vous pouvez créer une interface pour les cinq d'entre elles, et une classe abstraite pour ces trois avec le commun code.
Il existe également d'autres façons de partager l'implémentation, par exemple pour encapsuler un objet avec cette implémentation (par exemple dans le modèle Strategy ).
Vous déclareriez un résumé de classe lorsque vous ne voulez pas que le développeur (probablement vous-même) soit autorisé à l'instancier, car cela ne fonctionnerait pas ou n'aurait pas de sens.
Par exemple, considérons un jeu où il existe différents types d'entités de jeu. Ils héritent tous de la classe de base GameEntity
.
abstract class GameEntity{
int lifePoint, speed, damage;
public attack(GameEntity target){ target.damage(damage); }
public damage(int damageInflicted){ lifePoint -= damageInflicted - speed; }
// etc...
}
Cette classe est déclarée abstract
car cela n'aurait aucun sens de l'instancier. Il déclare certaines actions pour les entités de jeu et certains attributs, mais nulle part dans cette classe ces attributs ne sont initialisés. Cette classe sert de modèle pour les entités de jeu, mais n'est pas destinée à être instanciée seule, et comme telle déclarée abstract
.
Concernant la différence d'utilisation entre une classe abstraite et une interface:
Comme je le vois, une interface est un moyen d'obtenir un comportement polymorphe sans être limité par le mécanisme d'héritage unique de certains langages.
Revenons au jeu à titre d'exemple. Considérons une classe Enemy
qui est dérivée de GameEntity
. Cette classe a une méthode attackMeFromDistance(RangedAttacker attacker)
. Cette méthode vise à permettre aux entités d'attaquer l'ennemi de loin.
Comme vous pouvez le voir, cette méthode prend un type RangedAttacker
comme paramètre. Cependant, toutes les entités du jeu héritent déjà de GameEntity
. Ils ne peuvent pas prolonger une autre classe.
Prenez par exemple les classes Mage
et Archer
. Nous voulons permettre aux deux d'être acceptés comme paramètres dans la méthode attackMeFromDistance(RangedAttacker attacker)
, mais ils sont déjà dérivés de GameEntity
.
Pour résoudre cela, nous créons une nouvelle interface:
interface RangedAttacker{
public void attackFromDistance();
}
Une classe qui implémente cette interface doit implémenter la méthode attackFromDistance()
, et ainsi il est assuré qu'elle a des capacités d'attaque à distance. Cela signifie que la méthode attackMeFromDistance
peut désormais accepter en toute sécurité les classes qui implémentent cette interface. Donc, faire en sorte que Mage
et Archer
implémentent cette interface résout notre problème.
Pour moi, c'est la puissance des interfaces.
Donc, pour résumer, vous utiliseriez généralement une classe abstraite lorsque vous souhaitez avoir une classe de base pour certaines classes, mais cela n'aurait aucun sens de l'instancier d'elle-même (ou dans le cas où elle a abstract
, qui doivent être implémentées par les sous-classes, et dans ce cas le compilateur vous obligerait à créer la classe abstract
). Vous utiliseriez une interface pour obtenir un comportement polymorphe sans être limité par le mécanisme d'héritage unique.