Pourquoi les interfaces sont-elles préférées aux classes abstraites?
j'ai récemment assisté à une entrevue et ils m'ont posé la question "pourquoi les Interfaces sont préférées aux classes abstraites?"
j'ai essayé de donner quelques réponses comme:
- nous ne pouvons obtenir qu'une fonctionnalité étendue
- ils sont 100% Abstrait
- la mise en Œuvre n'est pas codée en dur
ils m'ont demandé de prendre l'api JDBC que vous utilisez. "Pourquoi sont-ils Les Interfaces?".
puis-je obtenir une meilleure réponse?
23 réponses
cette question d'entrevue reflète une certaine croyance de la personne qui pose la question. Je crois que la personne a tort, et donc vous pouvez aller dans l'une des deux directions.
- donnez-leur la réponse qu'ils veulent.
- respectueusement pas d'accord.
La réponse qu'ils veulent, eh bien, les autres affiches ont mis en évidence ces incroyablement bien. L'héritage d'interface Multiple, l'héritage force le classe pour faire des choix de mise en œuvre, les interfaces peuvent être modifiées plus facilement.
cependant, si vous créez un argument convaincant (et correct) dans votre désaccord, alors l'intervieweur pourrait prendre note. Tout d'abord, mettre en évidence les points positifs au sujet des interfaces, C'est un MUST. Deuxièmement, je dirais que les interfaces sont meilleures dans de nombreux scénarios, mais elles conduisent également à une duplication de code, ce qui est une chose négative. Si vous disposez d'un large éventail de sous-classes qui feront largement l' même implémentation, plus des fonctionnalités supplémentaires, alors vous pourriez vouloir une classe abstraite. Il vous permet d'avoir beaucoup d'objets similaires avec des détails à grain fin, alors qu'avec seulement des interfaces, vous devez avoir beaucoup d'objets distincts avec le code presque dupliqué.
Les Interfacesont de nombreuses utilisations, et il y a une raison impérieuse de croire qu'elles sont "meilleures". Cependant, vous devez toujours utiliser le bon outil pour le travail, et cela signifie que vous ne pouvez pas écrire hors de classes abstraites.
vous n'avez qu'une chance d'hériter. Si vous créez une classe abstraite plutôt qu'une interface, quelqu'un qui hérite de votre classe ne peut pas hériter d'une classe abstraite différente.
en général, et il ne s'agit nullement d'une "règle" à suivre aveuglément, l'arrangement le plus souple est:
interface
abstract class
concrete class 1
concrete class 2
l'interface est là pour deux raisons:
- une classe existante qui étend déjà quelque chose peut implémenter l'interface (en supposant que vous avez le contrôle sur le code pour la classe existante)
- une classe existante peut être des sous-classes et la sous-classe peut mettre en œuvre les interface (en supposant que la classe existante est sous-classable)
cela signifie que vous pouvez prendre des classes préexistantes (ou juste des classes qui doivent s'étendre à partir de quelque chose d'autre) et les faire fonctionner avec votre code.
la classe abstraite est là pour fournir tous les bits communs pour les classes concrètes. La classe abstract est étendue à partir du moment où vous écrivez de nouvelles classes ou modifiez des classes que vous voulez étendre (en supposant qu'elles s'étendent de Java.lang.Objet.)
vous devriez toujours (sauf si vous avez une bonne raison de ne pas le faire) déclarer les variables (par exemple, classe, local, et paramètres de méthode) comme interface.
vous pouvez implémenter plus d'une interface, mais vous ne pouvez hériter que d'une seule classe
Classes Abstraites
1.Ne peuvent pas être instanciées indépendamment de leurs classes dérivées. Classe abstraite les constructeurs sont appelés par leurs classes dérivées.
2.Définissez les signatures de membres abstraits que les classes de base doivent implémenter.
3.Sont plus extensibles que les interfaces, sans briser aucune compatibilité de version. Les classes abstraites, il est possible d'ajouter membres supplémentaires nonabstract que toutes les classes dérivées peuvent hériter.
4.Peut inclure des données stockées dans les champs.
5.Permettre des membres (virtuels) qui ont une implémentation et, par conséquent, fournir une implémentation par défaut d'un membre à la classe dérivante.
6.Dériver d'une classe abstraite utilise la seule et unique option de classe de base d'une sous-classe.
Interface
1.Ne peut pas être instanciée.
2.La mise en œuvre de tous les membres de l'interface se produit dans la classe de base. Il n'est pas possible de mettre en œuvre seulement quelques membres dans la classe de mise en œuvre.
3.L'extension des interfaces avec des membres supplémentaires casse la compatibilité de la version.
4.Impossible de stocker des données. Les champs ne peuvent être spécifiés que sur les classes dérivées. La solution de contournement pour ce qui est de définir les propriétés, mais sans implémentation.
5.Tous les membres sont automatiquement virtuels et ne peuvent inclure aucune implémentation.
6.Bien qu'aucune implémentation par défaut ne puisse apparaître, les classes implémentant des interfaces peuvent continuer à dériver les unes des autres.
Comme devinb et d'autres mentionnent, il semble que l'enquêteur montre leur ignorance de ne pas accepter votre réponse valable.
cependant, la mention de JDBC pourrait être un indice. Dans ce cas, peut-être demandent-ils les avantages d'un client codage par rapport à une interface au lieu d'une classe.
donc au lieu de réponses parfaitement valables comme " vous seulement obtenir une utilisation de l'héritage " , qui se rapportent à la conception de classe, ils peuvent être à la recherche d'une réponse plus comme " découpler un client d'une mise en œuvre spécifique " .
les classes abstraites présentent un certain nombre d'écueils potentiels. Par exemple, si vous outrepassez une méthode, la méthode super()
n'est pas appelée sauf si vous l'appelez explicitement. Cela peut causer des problèmes pour les classes dominantes mal mises en œuvre. En outre, il ya des problèmes potentiels avec equals()
lorsque vous utilisez l'héritage.
utiliser des interfaces peut encourager l'utilisation de la composition lorsque vous voulez partager une implémentation. La Composition est très souvent une meilleure façon de réutiliser d'autres objectent, car il est moins fragile. L'héritage est facilement surutilisé ou utilisé à de mauvaises fins.
définir une interface est une façon très sûre de définir comment un objet est censé agir, sans risquer la fragilité que peut venir avec l'extension d'une autre classe, abstraite ou non.
Aussi, comme vous le mentionnez, vous ne pouvez étendre une classe à un moment, mais vous pouvez implémenter autant d'interfaces que vous le souhaitez.
les classes Abstraites sont utilisés lorsque vous héritez mise en œuvre , les interfaces sont utilisées lorsque vous héritez cahier des charges . Les normes JDBC stipulent que "une connexion doit faire this ". C'est une spécification.
lorsque vous utilisez des classes abstraites, vous créez un couplage entre la sous-classe et la classe de base. Ce couplage peut parfois rendre le code très difficile à changer, d'autant plus que le nombre de sous-classes augmente. Les Interfaces n'ont pas ce problème.
vous aussi vous n'avez qu'un héritage, donc vous devez vous assurer que vous l'utilisez pour les bonnes raisons.
, "Pourquoi les Interfaces sont préférables Les classes abstraites?"
les autres messages ont fait un excellent travail de regarder les différences entre les interfaces et les classes abstraites, donc je ne vais pas dupliquer ces pensées.
mais en regardant la question d'entrevue, la meilleure question est vraiment " quand devrait-on préférer les interfaces aux classes abstraites?"(et vice versa).
comme pour la plupart des concepts de programmation, ils sont disponibles pour une raison et les énoncés absolus comme celui de la question d'entrevue ont tendance à manquer cela. Cela me rappelle en quelque sorte tout l'énoncé que vous avez utilisé pour lire concernant le goto énoncé en C. "Vous ne devriez jamais utiliser goto - il révèle de mauvaises compétences de codage."Cependant, goto toujours eu ses utilisations appropriées.
Respectueusement en désaccord avec la plupart des affiches ci-dessus (désolé! mod-moi si vous voulez :-) )
tout d'abord, la réponse" une seule super classe " est boiteuse. Tous ceux qui m'ont donné cette réponse dans une interview seraient rapidement contrés avec "C++ existait avant Java et C++ avait de multiples super classes. Pourquoi pensez-vous que James Gosling n'a autorisé Qu'une seule superclasse pour Java?"
Comprendre la philosophie derrière votre réponse sinon, vous êtes grillé (au moins, si je vous interviewer.)
Deuxièmement, les interfaces présentent de multiples avantages par rapport aux classes abstraites, en particulier lors de la conception des interfaces. Le plus important est de ne pas avoir une structure de classe particulière imposée à l'appelant d'une méthode. Il n'y a rien de pire que d'essayer d'utiliser une méthode qui exige une structure de classe particulière. C'est douloureux et embarrassant. En utilisant une interface n'importe quoi peut être passé à la méthode avec un minimum d'attentes.
exemple:
public void foo(Hashtable bar);
vs.
public void foo(Map bar);
pour le premier, l'appelant va toujours prendre leur structure de données existante et le claquer dans un nouveau Hashtable.
Troisièmement, les interfaces permettent que les méthodes publiques des exécutants de la classe de béton soient"privées". Si la méthode n'est pas déclarée dans l'interface, puis le la méthode ne peut pas être utilisée (ou mal utilisée) par les catégories qui n'ont pas d'entreprise utilisant la méthode. Ce qui m'amène au point 4....
Quatrièmement, les Interfaces représentent un contrat minimal entre la classe d'exécution et l'appelant. Ce contrat minimal spécifie exactement comment l'exécuteur de béton s'attend à être utilisé et pas plus. La classe d'appel n'est pas autorisée à utiliser une autre méthode non spécifiée par le" contrat " du interface. Le nom de l'interface utilisée flavore également l'attente du développeur sur la façon dont ils devraient utiliser l'objet. Si un développeur est passé un
public interface FragmentVisitor {
public void visit(Node node);
}
Le développeur sait que la seule méthode qu'ils peuvent appeler la visite de la méthode. Ils ne se laissent pas distraire par les méthodes brillantes dans la classe de béton avec laquelle ils ne devraient pas déconner.
enfin, les classes abstraites ont de nombreuses méthodes qui ne sont réellement présentes que pour les sous-classes à utiliser. Ainsi les classes abstraites ont tendance à ressembler un peu à un gâchis pour le développeur extérieur, il n'y a pas de directives sur les méthodes qui sont destinées à être utilisées par le code extérieur.
Oui bien sûr, certaines de ces méthodes peuvent être protégées. Cependant, les méthodes malheureusement protégées sont également visibles pour d'autres classes dans le même paquet. Et si la méthode d'une classe abstraite implémente une interface, la méthode doit être publique.
Cependant en utilisant toutes les interfaces ces entrailles qui sont accrochées en regardant la classe abstraite super ou la classe de béton sont cachées en toute sécurité.
Oui je sais que le développeur peut bien sûr utiliser certaines connaissances "spéciales" pour projeter un objet vers une autre interface plus large ou la classe de béton elle-même. Mais une telle distribution viole le contrat attendu, et le développeur devrait être giflé avec un saumon.
S'ils pensent que X est mieux que Y Je ne serais pas inquiet d'obtenir le travail, je n'aimerais pas travailler pour quelqu'un qui m'a forcé à un design plutôt qu'un autre parce qu'on leur a dit que les interfaces sont les meilleures. Les deux sont bons selon la situation, sinon pourquoi la langue a-t-elle choisi d'ajouter des classes abstraites? Les langagiers sont sûrement plus intelligents que moi.
C'est la question de "l'Héritage Multiple". Nous pouvons " étendre "pas plus d'une classe d'abstinence à la fois à travers une autre classe, mais dans les Interfaces, nous pouvons" implémenter " plusieurs interfaces dans une seule classe. Donc, bien que Java ne fournisse pas l'héritage multiple en général mais en utilisant des interfaces nous pouvons incorporer la propriété d'héritage multiplt en elle.
Espérons que cette aide!!!
interface
s sont une façon plus propre d'écrire une classe purement abstraite. Vous pouvez dire que la mise en œuvre n'est pas entrée en jeu (bien sûr, vous pourriez vouloir le faire à certaines étapes de la maintenance, ce qui rend les interfaces mauvaises). C'est à ce sujet. Il n'y a presque aucune différence perceptible au code client.
JDBC est un très mauvais exemple. Demandez à quiconque a essayé d'implémenter les interfaces et de maintenir le code entre les versions de JDK. JAX-WS est encore pire, en ajoutant des méthodes dans les versions de mise à jour.
il y a des différences techniques, telles que la possibilité de multiplier l'interface" inherit". Cela tend à être le résultat d'une conception Confuse. Dans de rares cas, il pourrait être utile d'avoir une hiérarchie d'implémentation différente de celle de l'interface.
sur l'inconvénient pour les interfaces, le compilateur est incapable de ramasser sur certains moulages impossibles/ instanceof
s.
il y a une raison qui n'est pas mentionnée ci-dessus.
vous pouvez décorer n'importe quelle interface facilement avec java.lang.refléter.Proxy vous permettant d'ajouter du code personnalisé à l'exécution de n'importe quelle méthode dans l'interface donnée. Il est très puissant.
voir http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html pour un tutoriel.
interface ne remplace pas classe abstraite .
Préfèrent
interface: pour mettre en œuvre un contrat par plusieurs objets indépendants
classe abstraite: Pour mettre en œuvre les mêmes ou différents comportements entre plusieurs objets liés à l'
Consultez à cette question se liée pour les cas d'utilisation à la fois de l'interface et de la classe abstraite
Interface vs Classe Abstraite générale (OO)
"151910920 cas d'Utilisation":
si vous devez utiliser Template_method pattern, vous ne pouvez pas réaliser avec interface. la classe Abstraite devraient être choisis pour l'atteindre.
si vous devez mettre en œuvre une capacité pour de nombreux objets non évalué, classe abstraite ne sert pas le but et vous devez choisir interface .
vous pouvez implémenter plusieurs interfaces, mais en particulier avec c# vous ne pouvez pas avoir plusieurs héritages
parce que les interfaces ne vous forcent pas dans une quelconque hiérarchie d'héritage.
vous définissez des interfaces lorsque vous n'avez besoin que d'un objet implémentant certaines méthodes mais que vous ne vous souciez pas de son pedigree. Ainsi, quelqu'un peut étendre une classe existante pour implémenter une interface, sans affecter le comportement existant de cette classe.
C'est pourquoi JDBC est toutes les interfaces; vous ne vous souciez pas vraiment des classes utilisées dans une implémentation JDBC, vous n'avez besoin que d'une implémentation JDBC pour avoir le même comportement attendu. En interne, l' Le pilote JDBC d'Oracle peut être très différent du pilote PostgreSQL, mais cela ne vous concerne pas. On peut avoir à hériter de certaines classes internes que les développeurs de base de données avaient déjà, tandis qu'un autre peut être entièrement développé à partir de zéro, mais ce n'est pas important pour vous tant qu'ils mettent en œuvre tous les deux les mêmes interfaces de sorte que vous pouvez communiquer avec l'un ou l'autre sans connaître le fonctionnement interne de l'un ou l'autre.
Eh bien, je suggère que la question elle-même devrait être reformulée. Les Interfaces sont principalement des contrats qu'une classe acquiert, la mise en œuvre de ce contrat lui-même va varier. Une classe abstraite contiendra généralement une certaine logique par défaut et ses classes enfants ajouteront plus de logique. Je dirais que la réponse aux questions dépend du problème du diamant. Java empêche l'héritage multiple pour l'éviter. ( http://en.wikipedia.org/wiki/Diamond_problem ).
ils m'ont demandé de prendre n'importe quelle api JDBC que vous utilisez. "Pourquoi sont-ils Les Interfaces?".
ma réponse à cette question spécifique est:
SUN ne sait pas comment les mettre en œuvre ou quoi mettre dans la mise en œuvre. C'est l'prestataires de services/db fournisseurs à mettre leur logique dans la mise en œuvre.
la conception JDBC a un rapport avec le modèle de pont, qui dit " découpler un abstraction de sa mise en œuvre pour que les deux puissent varier indépendamment".
signifie que la hiérarchie des interfaces de l'api JDBC peut être développée indépendamment de la hiérarchie d'implémentation qu'un fournisseur jdbc fournit ou utilise.
les classes abstraites offrent un moyen de définir un modèle de comportement, où les plugins utilisateur dans les détails.
un bon exemple est SwingWorker de Java 6 . Il définit un cadre pour faire quelque chose en arrière-plan, exigeant de l'utilisateur de définir doInBackground() pour la tâche réelle.
j'ai étendu cette classe de manière à créer automatiquement une barre de progression popup. J'ai remplacé done(), pour contrôler disposition de cette pop-up, mais a ensuite fourni un nouveau point de surpassement, permettant à l'utilisateur de définir optionnellement ce qui se passe après la barre de progression disparaît.
public abstract class ProgressiveSwingWorker<T, V> extends SwingWorker<T, V> {
private JFrame progress;
public ProgressiveSwingWorker(final String title, final String label) {
SwingUtilities.invokeLater(new Runnable() {
@SuppressWarnings("serial")
@Override
public void run() {
progress = new JFrame() {{
setLayout(new MigLayout("","[grow]"));
setTitle(title);
add(new JLabel(label));
JProgressBar bar = new JProgressBar();
bar.setIndeterminate(true);
add(bar);
pack();
setLocationRelativeTo(null);
setVisible(true);
}};
}
});
}
/**
* This method has been marked final to secure disposing of the progress dialog. Any behavior
* intended for this should be put in afterProgressBarDisposed.
*/
@Override
protected final void done() {
progress.dispose();
try {
afterProgressBarDisposed(get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
protected void afterProgressBarDisposed(T results) {
}
}
l'Utilisateur a toujours l'exigence de fournir la mise en œuvre de doInBackground () . Cependant, ils peuvent aussi avoir un comportement de suivi, comme ouvrir une autre fenêtre, afficher un JOptionPane avec des résultats, ou tout simplement ne rien faire.
pour l'utiliser:
new ProgressiveSwingWorker<DataResultType, Object>("Editing some data", "Editing " + data.getSource()) {
@Override
protected DataResultType doInBackground() throws Exception {
return retrieve(data.getSource());
}
@Override
protected void afterProgressBarDisposed(DataResultType results) {
new DataEditor(results);
}
}.execute();
cela montre comment une classe abstraite peut très bien fournir une opération templée, orthogonale au concept d'interfaces définissant un contrat API.
Cela dépend de votre exigence et de votre capacité de mise en œuvre, ce qui est très important. Vous avez tellement de réponses à cette question. Ce que je pense de cette question Est que la classe abstraite est l'évolution de L'API. Vous pouvez définir votre future définition de fonction dans la classe abstraite, mais vous n'avez pas besoin de toute l'implémentation de fonction dans votre classe principale, mais avec l'interface vous ne pouvez pas faire cette chose.