J'ai vu cela mentionné plusieurs fois et je ne sais pas ce que cela signifie. Quand et pourquoi feriez-vous cela?
Je sais ce que font les interfaces, mais le fait que je ne sois pas clair à ce sujet me fait penser que je manque de les utiliser correctement.
En est-il de même si vous deviez faire:
IInterface classRef = new ObjectWhatever()
Vous pourriez utiliser n'importe quelle classe qui implémente IInterface
? Quand auriez-vous besoin de faire cela? La seule chose à laquelle je peux penser, c'est si vous avez une méthode et que vous n'êtes pas sûr de quel objet sera passé, sauf pour sa mise en œuvre IInterface
. Je ne peux pas penser à quelle fréquence vous auriez besoin de le faire.
Aussi, comment pourriez-vous écrire une méthode qui prend un objet qui implémente une interface? Est-ce possible?
la source
Réponses:
Il y a ici de merveilleuses réponses à ces questions qui entrent dans toutes sortes de détails sur les interfaces et le couplage lâche du code, l'inversion du contrôle, etc. Il y a des discussions assez capiteuses, donc j'aimerais profiter de l'occasion pour décomposer un peu les choses pour comprendre pourquoi une interface est utile.
Lorsque j'ai commencé à être exposé aux interfaces, j'étais moi aussi confus quant à leur pertinence. Je ne comprenais pas pourquoi tu en avais besoin. Si nous utilisons un langage comme Java ou C #, nous avons déjà l'héritage et j'ai considéré les interfaces comme une forme d'héritage plus faible et je me suis dit "pourquoi s'embêter?" Dans un sens, j'avais raison, vous pouvez considérer les interfaces comme une sorte de forme d'héritage faible, mais au-delà de cela, j'ai finalement compris leur utilisation en tant que construction de langage en les considérant comme un moyen de classer les traits ou comportements communs qui étaient exposés par potentiellement de nombreuses classes d'objets non liés.
Par exemple, disons que vous avez un jeu SIM et que vous avez les classes suivantes:
Il est clair que ces deux objets n'ont rien de commun en termes d'héritage direct. Mais, on pourrait dire qu'ils sont tous les deux ennuyeux.
Disons que notre jeu doit avoir une sorte de chose aléatoire qui agace le joueur quand il mange. Cela peut être un
HouseFly
ou unTelemarketer
ou les deux - mais comment autorisez-vous les deux avec une seule fonction? Et comment demandez-vous à chaque type d'objet différent de "faire sa chose ennuyeuse" de la même manière?La clé à réaliser est que a
Telemarketer
etHouseFly
partagent un comportement commun mal interprété, même s'ils ne sont rien en termes de modélisation. Faisons donc une interface que les deux peuvent implémenter:Nous avons maintenant deux classes qui peuvent chacune être gênantes à leur manière. Et ils n'ont pas besoin de dériver de la même classe de base et de partager des caractéristiques inhérentes communes - ils doivent simplement satisfaire le contrat de
IPest
- ce contrat est simple. Tu dois justeBeAnnoying
. À cet égard, nous pouvons modéliser les éléments suivants:Ici, nous avons une salle à manger qui accepte un certain nombre de convives et un certain nombre de parasites - notez l'utilisation de l'interface. Cela signifie que dans notre petit monde, un membre du
pests
tableau pourrait en fait être unTelemarketer
objet ou unHouseFly
objet.La
ServeDinner
méthode est appelée lorsque le dîner est servi et que nos gens dans la salle à manger sont censés manger. Dans notre petit jeu, c'est à ce moment que nos nuisibles font leur travail - chaque nuisible est invité à être ennuyeux via l'IPest
interface. De cette façon, nous pouvons facilement avoir les deuxTelemarketers
etHouseFlys
être ennuyeux de chacune de leurs manières - nous nous soucions seulement que nous ayons quelque chose dans l'DiningRoom
objet qui soit un parasite, nous ne nous soucions pas vraiment de ce qu'il est et ils ne pourraient rien avoir dans commun avec d'autres.Cet exemple de pseudo-code très artificiel (qui a traîné beaucoup plus longtemps que prévu) est simplement destiné à illustrer le genre de chose qui a finalement allumé la lumière pour moi en termes de moment où nous pourrions utiliser une interface. Je m'excuse à l'avance pour la bêtise de l'exemple, mais j'espère que cela vous aidera à comprendre. Et, bien sûr, les autres réponses publiées que vous avez reçues ici couvrent vraiment la gamme de l'utilisation des interfaces aujourd'hui dans les modèles de conception et les méthodologies de développement.
la source
BeAnnoying
en œuvre en tant que no-op; cette interface peut exister à la place ou en plus de l'interface pour les choses gênantes (si les deux interfaces existent, l'interface "choses qui sont gênantes" devrait hériter de l'interface "choses qui pourraient être gênantes"). L'inconvénient de l'utilisation de telles interfaces est que les implémentations peuvent être gênées par l'implémentation d'un nombre "ennuyeux" de méthodes de stub. L'avantage est que ...IPest[]
précisez pas que parce que les objets dans sont des références IPest, vous pouvez appelerBeAnnoying()
parce qu'ils ont cette méthode, alors que vous ne pouvez pas appeler d'autres méthodes sans transtypage. Cependant, chaqueBeAnnoying()
méthode individuelle des objets sera appelée.L'exemple spécifique que j'ai donné aux étudiants est qu'ils doivent écrire
au lieu de
Ceux-ci se ressemblent exactement dans un programme court, mais si vous continuez à utiliser
myList
100 fois dans votre programme, vous pouvez commencer à voir une différence. La première déclaration garantit que vous n'appelez que des méthodesmyList
définies par l'List
interface (donc pas deArrayList
méthodes spécifiques). Si vous avez programmé l'interface de cette façon, plus tard, vous pourrez décider que vous avez vraiment besoinet vous n'avez qu'à changer votre code à cet endroit. Vous savez déjà que le reste de votre code ne fait rien qui sera cassé en changeant l' implémentation parce que vous avez programmé l' interface .
Les avantages sont encore plus évidents (je pense) lorsque vous parlez de paramètres de méthode et de valeurs de retour. Prenez ceci par exemple:
Cette déclaration de méthode vous lie à deux implémentations concrètes (
ArrayList
etHashMap
). Dès que cette méthode est appelée à partir d'un autre code, toute modification de ces types signifie probablement que vous devrez également modifier le code appelant. Il serait préférable de programmer sur les interfaces.Peu importe le type de
List
retour que vous faites ou le type deMap
paramètre transmis. Les modifications que vous apportez à l'intérieur de ladoSomething
méthode ne vous forceront pas à modifier le code appelant.la source
La programmation sur une interface signifie: "J'ai besoin de cette fonctionnalité et je m'en fiche d'où elle vient."
Considérez (en Java), l'
List
interface par rapport aux classesArrayList
etLinkedList
concrètes. Si tout ce qui m'importe, c'est que j'ai une structure de données contenant plusieurs éléments de données auxquels je dois accéder via l'itération, je choisirais unList
(et c'est 99% du temps). Si je sais que j'ai besoin d'insertion / suppression à temps constant de l'une ou l'autre extrémité de la liste, je pourrais choisir l'LinkedList
implémentation concrète (ou plus probablement, utiliser l' interface de file d'attente ). Si je sais que j'ai besoin d'un accès aléatoire par index, je choisirais laArrayList
classe concrète.la source
L'utilisation d'interfaces est un facteur clé pour rendre votre code facilement testable en plus de supprimer les couplages inutiles entre vos classes. En créant une interface qui définit les opérations sur votre classe, vous autorisez les classes qui souhaitent utiliser cette fonctionnalité à l'utiliser sans dépendre directement de votre classe d'implémentation. Si par la suite vous décidez de modifier et d'utiliser une implémentation différente, vous n'avez qu'à modifier la partie du code où l'implémentation est instanciée. Le reste du code n'a pas besoin de changer car il dépend de l'interface, pas de la classe d'implémentation.
Ceci est très utile pour créer des tests unitaires. Dans la classe sous test, vous devez la faire dépendre de l'interface et injecter une instance de l'interface dans la classe (ou une fabrique qui lui permet de construire des instances de l'interface selon les besoins) via le constructeur ou un configurateur de propriété. La classe utilise l'interface fournie (ou créée) dans ses méthodes. Lorsque vous allez écrire vos tests, vous pouvez simuler ou simuler l'interface et fournir une interface qui répond avec les données configurées dans votre test unitaire. Vous pouvez le faire car votre classe en cours de test ne traite que de l'interface, pas de votre implémentation concrète. Toute classe implémentant l'interface, y compris votre classe factice ou fausse, fera l'affaire.
EDIT: Ci-dessous est un lien vers un article où Erich Gamma discute de sa citation, "Programmer vers une interface, pas une implémentation."
http://www.artima.com/lejava/articles/designprinciples.html
la source
La programmation d'une interface n'a absolument rien à voir avec les interfaces abstraites comme nous le voyons en Java ou .NET. Ce n'est même pas un concept de POO.
Ce que cela signifie, c'est de ne pas jouer avec les éléments internes d'un objet ou d'une structure de données. Utilisez l'interface de programme abstraite ou API pour interagir avec vos données. En Java ou C #, cela signifie utiliser des propriétés et des méthodes publiques au lieu d'un accès brut aux champs. Pour C, cela signifie utiliser des fonctions au lieu de pointeurs bruts.
EDIT: Et avec les bases de données, cela signifie utiliser des vues et des procédures stockées au lieu d'un accès direct à la table.
la source
Vous devriez vous pencher sur l'inversion du contrôle:
Dans un tel scénario, vous n'écririez pas ceci:
Vous écririez quelque chose comme ceci:
Cela irait dans une configuration basée sur des règles dans l'
container
objet et construirait l'objet réel pour vous, qui pourrait être ObjectWthing. L'important est que vous puissiez remplacer cette règle par quelque chose qui utilisait un autre type d'objet, et votre code fonctionnerait toujours.Si nous laissons IoC hors de la table, vous pouvez écrire du code qui sait qu'il peut parler à un objet qui fait quelque chose de spécifique , mais pas quel type d'objet ni comment il le fait.
Cela serait utile lors du passage de paramètres.
Quant à votre question entre parenthèses "Comment pourriez-vous écrire une méthode qui prend un objet qui implémente une interface? Est-ce possible?", En C #, vous utiliseriez simplement le type d'interface pour le type de paramètre, comme ceci:
Cela se branche directement dans le «parler à un objet qui fait quelque chose de spécifique». La méthode définie ci-dessus sait à quoi s'attendre de l'objet, qu'elle implémente tout dans IInterface, mais peu importe de quel type d'objet il s'agit, seulement qu'elle adhère au contrat, c'est-à-dire ce qu'est une interface.
Par exemple, vous êtes probablement familier avec les calculatrices et en avez probablement utilisé plusieurs au cours de vos journées, mais la plupart du temps elles sont toutes différentes. Vous, d'autre part, savez comment une calculatrice standard devrait fonctionner, vous pouvez donc toutes les utiliser, même si vous ne pouvez pas utiliser les fonctionnalités spécifiques de chaque calculatrice qu'aucune autre n'a.
C'est la beauté des interfaces. Vous pouvez écrire un morceau de code, qui sait qu'il recevra des objets dont il peut attendre certains comportements. Peu importe le type d'objet, il suffit de prendre en charge le comportement nécessaire.
Permettez-moi de vous donner un exemple concret.
Nous avons un système de traduction sur mesure pour les formulaires Windows. Ce système parcourt les contrôles d'un formulaire et traduit le texte de chacun. Le système sait comment gérer les contrôles de base, comme la propriété-type-de-contrôle-qui-a-un-texte, et des trucs de base similaires, mais pour tout élément de base, il ne suffit pas.
Maintenant, comme les contrôles héritent de classes prédéfinies sur lesquelles nous n'avons aucun contrôle, nous pouvons effectuer l'une des trois choses suivantes:
Nous avons donc fait nr. 3. Tous nos contrôles implémentent ILocalizable, qui est une interface qui nous donne une méthode, la capacité de traduire "lui-même" dans un conteneur de texte / règles de traduction. En tant que tel, le formulaire n'a pas besoin de savoir quel type de contrôle il a trouvé, seulement qu'il implémente l'interface spécifique, et sait qu'il existe une méthode où il peut appeler pour localiser le contrôle.
la source
Code à l'interface Pas l'implémentation n'a rien à voir avec Java, ni sa construction d'interface.
Ce concept a été mis en évidence dans les livres Patterns / Gang of Four, mais il était très probablement là bien avant cela. Le concept existait certainement bien avant que Java n'existe.
La construction de l'interface Java a été créée pour aider à cette idée (entre autres), et les gens sont devenus trop concentrés sur la construction en tant que centre du sens plutôt que sur l'intention d'origine. Cependant, c'est la raison pour laquelle nous avons des méthodes et des attributs publics et privés en Java, C ++, C #, etc.
Cela signifie simplement interagir avec l'interface publique d'un objet ou d'un système. Ne vous inquiétez pas et ne prévoyez même pas comment il fait ce qu'il fait en interne. Ne vous inquiétez pas de la façon dont il est mis en œuvre. Dans le code orienté objet, c'est pourquoi nous avons des méthodes / attributs publics vs privés. Nous sommes destinés à utiliser les méthodes publiques car les méthodes privées ne sont là que pour une utilisation interne, au sein de la classe. Ils constituent l'implémentation de la classe et peuvent être modifiés au besoin sans changer l'interface publique. Supposons qu'en ce qui concerne la fonctionnalité, une méthode sur une classe effectuera la même opération avec le même résultat attendu chaque fois que vous l'appelez avec les mêmes paramètres. Il permet à l'auteur de changer le fonctionnement de la classe, son implémentation, sans casser la façon dont les gens interagissent avec elle.
Et vous pouvez programmer à l'interface, pas à l'implémentation sans jamais utiliser une construction Interface. Vous pouvez programmer sur l'interface et non sur l'implémentation en C ++, qui n'a pas de construction Interface. Vous pouvez intégrer deux systèmes d'entreprise massifs de manière beaucoup plus robuste tant qu'ils interagissent via des interfaces publiques (contrats) plutôt que d'appeler des méthodes sur des objets internes aux systèmes. Les interfaces devraient toujours réagir de la même manière attendue compte tenu des mêmes paramètres d'entrée; s'il est implémenté sur l'interface et non sur l'implémentation. Le concept fonctionne dans de nombreux endroits.
Ébranlez la pensée que les interfaces Java ont quoi que ce soit à voir avec le concept de «programme à l'interface, pas la mise en œuvre». Ils peuvent aider à appliquer le concept, mais ils ne sont pas le concept.
la source
Il semble que vous compreniez comment fonctionnent les interfaces mais que vous ne savez pas quand les utiliser ni quels avantages elles offrent. Voici quelques exemples de cas où une interface aurait un sens:
alors je pourrais créer GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider, etc.
puis créez JpegImageLoader, GifImageLoader, PngImageLoader, etc.
La plupart des compléments et des plugins fonctionnent hors interfaces.
Une autre utilisation populaire est le modèle de référentiel. Dites que je veux charger une liste de codes postaux provenant de différentes sources
je pourrais ensuite créer un XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository, etc. Une fois la base de données prête, j'écris un SQLRepository pour remplacer la version XML. Le reste de mon code reste inchangé car il fonctionne uniquement hors des interfaces.
Les méthodes peuvent accepter des interfaces telles que:
la source
Cela rend votre code beaucoup plus extensible et plus facile à maintenir lorsque vous avez des ensembles de classes similaires. Je suis un programmeur junior, donc je ne suis pas un expert, mais je viens de terminer un projet qui nécessitait quelque chose de similaire.
Je travaille sur un logiciel côté client qui parle à un serveur exécutant un appareil médical. Nous développons une nouvelle version de cet appareil qui a de nouveaux composants que le client doit parfois configurer. Il existe deux types de nouveaux composants, et ils sont différents, mais ils sont également très similaires. Fondamentalement, j'ai dû créer deux formulaires de configuration, deux classes de listes, deux de tout.
J'ai décidé qu'il serait préférable de créer une classe de base abstraite pour chaque type de contrôle qui contiendrait presque toute la logique réelle, puis des types dérivés pour prendre soin des différences entre les deux composants. Cependant, les classes de base n'auraient pas pu effectuer d'opérations sur ces composants si je devais me soucier des types tout le temps (enfin, ils auraient pu, mais il y aurait eu une instruction "if" ou un commutateur dans chaque méthode) .
J'ai défini une interface simple pour ces composants et toutes les classes de base parlent à cette interface. Maintenant, quand je change quelque chose, cela fonctionne à peu près partout et je n'ai pas de duplication de code.
la source
Beaucoup d'explications là-bas, mais pour le rendre encore plus simple. Prenez par exemple a
List
. On peut implémenter une liste avec comme:En créant une interface, dites a
List
. Vous ne codez que la définition de la liste ou ce que celaList
signifie en réalité.Vous pouvez utiliser n'importe quel type d'implémentation en interne, par exemple une
array
implémentation. Mais supposons que vous souhaitiez modifier l'implémentation pour une raison quelconque, par exemple un bug ou des performances. Il vous suffit ensuite de modifier la déclarationList<String> ls = new ArrayList<String>()
enList<String> ls = new LinkedList<String>()
.Nulle part ailleurs dans le code, ne devrez-vous changer quoi que ce soit d'autre; Parce que tout le reste a été construit sur la définition de
List
.la source
Si vous programmez en Java, JDBC est un bon exemple. JDBC définit un ensemble d'interfaces mais ne dit rien sur l'implémentation. Vos applications peuvent être écrites sur cet ensemble d'interfaces. En théorie, vous choisissez un pilote JDBC et votre application fonctionnera simplement. Si vous découvrez qu'il existe un pilote JDBC plus rapide ou "meilleur" ou moins cher ou pour quelque raison que ce soit, vous pouvez à nouveau en théorie reconfigurer votre fichier de propriétés, et sans avoir à modifier votre application, votre application fonctionnerait toujours.
la source
La programmation vers les interfaces est géniale, elle favorise un couplage lâche. Comme l'a mentionné @lassevk, l'inversion de contrôle en est une excellente utilisation.
En outre, examinez les principes SOLID . voici une série vidéo
Il passe par un codage en dur (exemple fortement couplé) puis examine les interfaces, pour enfin passer à un outil IoC / DI (NInject)
la source
Je suis arrivé tardivement à cette question, mais je tiens à mentionner ici que la ligne "Programmer vers une interface, pas une implémentation" a fait l'objet d'une bonne discussion dans le livre GoF (Gang of Four) Design Patterns.
Elle a déclaré, à la p. 18:
et surtout, cela a commencé par:
En d'autres termes, ne l'écrivez pas dans vos classes pour qu'il ait une
quack()
méthode pour les canards, puis unebark()
méthode pour les chiens, car elles sont trop spécifiques pour une implémentation particulière d'une classe (ou sous-classe). Au lieu de cela, écrivez la méthode en utilisant des noms suffisamment généraux pour être utilisés dans la classe de base, tels quegiveSound()
oumove()
, afin qu'ils puissent être utilisés pour les canards, les chiens ou même les voitures, puis le client de vos classes peut simplement dire.giveSound()
plutôt que penser à utiliserquack()
oubark()
même à déterminer le type avant d'émettre le bon message à envoyer à l'objet.la source
En plus de la réponse déjà sélectionnée (et des divers articles informatifs ici), je recommande fortement de prendre une copie des modèles de conception Head First . C'est une lecture très facile et répondra directement à votre question, expliquera pourquoi elle est importante et vous montrera de nombreux modèles de programmation que vous pouvez utiliser pour utiliser ce principe (et d'autres).
la source
Pour ajouter aux publications existantes, le codage des interfaces aide parfois sur les grands projets lorsque les développeurs travaillent simultanément sur des composants distincts. Tout ce dont vous avez besoin est de définir des interfaces à l'avance et de leur écrire du code pendant que d'autres développeurs écrivent du code sur l'interface que vous implémentez.
la source
C'est aussi bon pour les tests unitaires, vous pouvez injecter vos propres classes (qui répondent aux exigences de l'interface) dans une classe qui en dépend
la source
Il peut être avantageux de programmer sur des interfaces, même lorsque nous ne dépendons pas d'abstractions.
La programmation d'interfaces nous oblige à utiliser un sous-ensemble contextuellement approprié d'un objet . Cela aide parce que:
Par exemple, considérons une
Person
classe qui implémenteFriend
l'Employee
interface et.Dans le cadre de l'anniversaire de la personne, nous programmons sur l'
Friend
interface, pour éviter de traiter la personne comme uneEmployee
.Dans le cadre du travail de la personne, nous programmons sur l'
Employee
interface, pour éviter de brouiller les frontières du lieu de travail.Génial. Nous nous sommes comportés de manière appropriée dans différents contextes et notre logiciel fonctionne bien.
Dans un avenir lointain, si notre entreprise change pour travailler avec des chiens, nous pouvons changer le logiciel assez facilement. Tout d'abord, nous créons une
Dog
classe qui implémente à la foisFriend
etEmployee
. Ensuite, nous passons en toute sécuriténew Person()
ànew Dog()
. Même si les deux fonctions ont des milliers de lignes de code, cette simple modification fonctionnera car nous savons que les éléments suivants sont vrais:party
utilise uniquement leFriend
sous - ensemble dePerson
.workplace
utilise uniquement leEmployee
sous - ensemble dePerson
.Dog
implémente les interfacesFriend
etEmployee
.D'un autre côté, si l'un
party
ou l' autreworkplace
devait programmer contrePerson
, il y aurait un risque que les deux aient unPerson
code spécifique. Passer dePerson
àDog
nous obligerait à passer au peigne fin le code pour extirper toutPerson
code spécifiqueDog
ne prend pas en charge.Moralité : la programmation des interfaces aide notre code à se comporter correctement et à être prêt pour le changement. Il prépare également notre code à dépendre des abstractions, ce qui apporte encore plus d'avantages.
la source
Si j'écris une nouvelle classe
Swimmer
pour ajouter la fonctionnalitéswim()
et besoin d'utiliser un objet de classe disonsDog
, et cetteDog
classe implémente l'interfaceAnimal
qui déclareswim()
.En haut de la hiérarchie (
Animal
), c'est très abstrait tandis qu'en bas (Dog
) c'est très concret. La façon dont je pense à la «programmation vers les interfaces» est que, lorsque j'écris uneSwimmer
classe, je veux écrire mon code par rapport à l'interface qui est aussi loin dans cette hiérarchie qui dans ce cas est unAnimal
objet. Une interface est exempte de détails d'implémentation et rend ainsi votre code faiblement couplé.Les détails de l'implémentation peuvent être modifiés avec le temps, cependant, cela n'affectera pas le code restant puisque tout ce avec quoi vous interagissez est avec l'interface et non l'implémentation. Vous ne vous souciez pas de l'implémentation ... tout ce que vous savez, c'est qu'il y aura une classe qui implémenterait l'interface.
la source
Donc, juste pour bien comprendre, l'avantage d'une interface est que je peux séparer l'appel d'une méthode de n'importe quelle classe particulière. Au lieu de cela, créez une instance de l'interface, où l'implémentation est donnée à partir de la classe que je choisis qui implémente cette interface. Cela me permet donc d'avoir de nombreuses classes, qui ont des fonctionnalités similaires mais légèrement différentes et dans certains cas (les cas liés à l'intention de l'interface) ne se soucient pas de quel objet il s'agit.
Par exemple, je pourrais avoir une interface de mouvement. Une méthode qui fait «bouger» quelque chose et tout objet (personne, voiture, chat) qui implémente l'interface de mouvement pourrait être transmise et ordonnée de bouger. Sans la méthode, tout le monde connaît le type de classe.
la source
Imaginez que vous ayez un produit appelé «Zebra» qui peut être étendu par des plugins. Il trouve les plugins en recherchant des DLL dans certains répertoires. Il charge toutes ces DLL et utilise la réflexion pour trouver toutes les classes qui implémentent
IZebraPlugin
, puis appelle les méthodes de cette interface pour communiquer avec les plugins.Cela le rend complètement indépendant de toute classe de plugin spécifique - peu importe ce que sont les classes. Il se soucie seulement qu'ils remplissent les spécifications de l'interface.
Les interfaces sont un moyen de définir des points d'extensibilité comme celui-ci. Le code qui parle à une interface est couplé de manière plus lâche - en fait, il n'est pas du tout couplé à un autre code spécifique. Il peut interagir avec des plugins écrits des années plus tard par des personnes qui n'ont jamais rencontré le développeur d'origine.
Vous pouvez à la place utiliser une classe de base avec des fonctions virtuelles - tous les plugins seront dérivés de la classe de base. Mais cela est beaucoup plus limitatif car une classe ne peut avoir qu'une seule classe de base, alors qu'elle peut implémenter n'importe quel nombre d'interfaces.
la source
Explication C ++.
Considérez une interface comme vos méthodes publiques de classe.
Vous pouvez ensuite créer un modèle qui «dépend» de ces méthodes publiques afin d'exécuter sa propre fonction (il rend les appels de fonction définis dans l'interface publique des classes). Disons que ce modèle est un conteneur, comme une classe Vector, et l'interface dont il dépend est un algorithme de recherche.
Toute classe d'algorithme qui définit les fonctions / interface que Vector appelle pour satisfaire le «contrat» (comme quelqu'un l'a expliqué dans la réponse d'origine). Les algorithmes n'ont même pas besoin d'être de la même classe de base; la seule exigence est que les fonctions / méthodes dont dépend le vecteur (interface) soient définies dans votre algorithme.
Le point de tout cela est que vous pouvez fournir n'importe quel algorithme / classe de recherche différent tant qu'il fournit l'interface dont Vector dépend (recherche de bulles, recherche séquentielle, recherche rapide).
Vous pouvez également vouloir concevoir d'autres conteneurs (listes, files d'attente) qui exploiteraient le même algorithme de recherche que Vector en les faisant remplir l'interface / le contrat dont dépendent vos algorithmes de recherche.
Cela vous fait gagner du temps (principe de réutilisation du code OOP) car vous pouvez écrire un algorithme une fois au lieu de plusieurs fois spécifique à chaque nouvel objet que vous créez sans trop compliquer le problème avec une arborescence d'héritage envahie.
Quant à «manquer» la façon dont les choses fonctionnent; big-time (au moins en C ++), car c'est ainsi que la plupart du framework de la bibliothèque standard TEMPLATE fonctionne.
Bien sûr, lorsque vous utilisez l'héritage et les classes abstraites, la méthodologie de programmation d'une interface change; mais le principe est le même, vos fonctions / méthodes publiques sont votre interface de classes.
C'est un sujet énorme et l'un des principes fondamentaux des modèles de conception.
la source
En Java, ces classes concrètes implémentent toutes l'interface CharSequence:
Ces classes concrètes n'ont pas de classe parente commune autre que Object, donc il n'y a rien qui les relie, à part le fait qu'elles ont chacune quelque chose à voir avec des tableaux de caractères, représentant ou manipulant tel. Par exemple, les caractères de String ne peuvent pas être modifiés une fois qu'un objet String est instancié, tandis que les caractères de StringBuffer ou StringBuilder peuvent être modifiés.
Pourtant, chacune de ces classes est capable d'implémenter convenablement les méthodes d'interface CharSequence:
Dans certains cas, les classes de bibliothèque de classes Java qui acceptaient autrefois String ont été révisées pour accepter maintenant l'interface CharSequence. Donc, si vous avez une instance de StringBuilder, au lieu d'extraire un objet String (ce qui signifie instancier une nouvelle instance d'objet), il peut simplement passer le StringBuilder lui-même lors de l'implémentation de l'interface CharSequence.
L'interface Appendable que certaines classes implémentent a à peu près le même type d'avantage pour toute situation où des caractères peuvent être ajoutés à une instance de l'instance d'objet de classe concrète sous-jacente. Toutes ces classes concrètes implémentent l'interface Appendable:
la source
CharSequence
soient si anémiques. Je souhaite que Java et .NET aient permis aux interfaces d'avoir une implémentation par défaut, afin que les gens ne réduisent pas les interfaces uniquement dans le but de minimiser le code standard. Étant donné touteCharSequence
implémentation légitime, on pourrait émuler la plupart des fonctions enString
utilisant uniquement les quatre méthodes ci-dessus, mais de nombreuses implémentations pourraient exécuter ces fonctions de manière beaucoup plus efficace par d'autres moyens. Malheureusement, même si une implémentation particulière deCharSequence
contient tout en un seulchar[]
et pourrait en exécuter plusieurs ...indexOf
rapidement, il n'y a aucun moyen qu'un appelant qui n'est pas familier avec une implémentation particulièreCharSequence
puisse lui demander de le faire plutôt que d'avoir à utilisercharAt
pour examiner chaque caractère individuel.Petite histoire: Un facteur est prié de rentrer chez lui après la maison et de recevoir les couvertures (lettres, documents, chèques, cartes-cadeaux, application, lettre d'amour) avec l'adresse écrite dessus pour la livraison.
Supposons qu'il n'y ait pas de couverture et demandez au facteur de rentrer chez lui après la maison et de recevoir toutes les choses et de livrer à d'autres personnes, le facteur peut devenir confus.
Il vaut donc mieux l'envelopper avec une couverture (dans notre histoire, c'est l'interface), alors il fera bien son travail.
Maintenant, le travail du facteur est de recevoir et de livrer les couvertures uniquement (il ne dérangerait pas ce qu'il y a à l'intérieur de la couverture).
Créez un type de type
interface
non réel, mais implémentez-le avec le type réel.Créer à l'interface signifie que vos composants s'intègrent facilement dans le reste du code
Je vous donne un exemple.
vous avez l'interface AirPlane comme ci-dessous.
Supposons que vous ayez des méthodes dans votre classe de contrôleur comme
et
mis en œuvre dans votre programme. Cela ne cassera pas votre code. Je veux dire, il n'a pas besoin de changer tant qu'il accepte les arguments
AirPlane
.Parce qu'il acceptera tout avion malgré type réel,
flyer
,highflyr
,fighter
, etc.Aussi, dans une collection:
List<Airplane> plane;
// Prendra tous vos avions.L'exemple suivant clarifiera votre compréhension.
Vous avez un avion de chasse qui le met en œuvre, donc
La même chose pour HighFlyer et autres clasess:
Pensez maintenant à vos classes de contrôleur en utilisant
AirPlane
plusieurs fois,Supposons que votre classe Controller soit ControlPlane comme ci-dessous,
Ici, la magie vient car vous pouvez créer
AirPlane
autant d'instances que vous le souhaitez et vous ne changez pas le code de laControlPlane
classe.Vous pouvez ajouter une instance ...
Vous pouvez également supprimer des instances de types créés précédemment.
la source
Une interface est comme un contrat, où vous voulez que votre classe d'implémentation implémente des méthodes écrites dans le contrat (interface). Étant donné que Java ne fournit pas d'héritage multiple, la «programmation vers l'interface» est un bon moyen de réaliser l'héritage multiple.
Si vous avez une classe A qui étend déjà une autre classe B, mais que vous souhaitez que cette classe A suive également certaines directives ou implémente un certain contrat, vous pouvez le faire par la stratégie de "programmation pour l'interface".
la source
Remarque: Nous n'avons pas pu instancier une interface non implémentée par une classe - True.
Remarque: Maintenant, nous pouvions comprendre ce qui se passait si Bclass et Cclass implémentaient le même Dintf.
Ce que nous avons: mêmes prototypes d'interface (noms de fonctions dans l'interface), et appeler différentes implémentations.
Bibliographie: prototypes - wikipedia
la source
Le programme vers une interface permet de modifier la mise en œuvre du contrat défini par l'interface de manière transparente. Il permet un couplage lâche entre contrat et implémentations spécifiques.
Jetez un oeil à cette question SE pour un bon exemple.
Pourquoi l'interface pour une classe Java devrait-elle être préférée?
Oui. Il aura une légère surcharge de performances en moins de secondes. Mais si votre application doit modifier dynamiquement l'implémentation de l'interface, ne vous inquiétez pas de l'impact sur les performances.
N'essayez pas d'éviter plusieurs implémentations d'interface si votre application en a besoin. En l'absence de couplage étroit de l'interface avec une implémentation spécifique, vous devrez peut-être déployer le correctif pour changer une implémentation en une autre implémentation.
Un bon cas d'utilisation: mise en œuvre du modèle de stratégie:
Exemple réel du modèle de stratégie
la source
programme à une interface est un terme du livre GOF. je ne dirais pas directement que cela a à voir avec l'interface java mais plutôt avec de vraies interfaces. pour réaliser une séparation nette des couches, vous devez créer une séparation entre les systèmes, par exemple: supposons que vous ayez une base de données concrète que vous souhaitez utiliser, vous ne "programmez jamais à la base de données", au lieu de cela, vous "programmez à l'interface de stockage". De même, vous ne programmez jamais vers un service Web, mais vous programmez plutôt vers une «interface client». c'est pour que vous puissiez facilement échanger les choses.
je trouve que ces règles m'aident:
1 . nous utilisons une interface java lorsque nous avons plusieurs types d'objet. si je n'ai qu'un seul objet, je ne vois pas le point. s'il y a au moins deux implémentations concrètes d'une idée, j'utiliserais une interface java.
2 . si, comme je l'ai indiqué ci-dessus, vous souhaitez apporter le découplage d'un système externe (système de stockage) à votre propre système (base de données locale), utilisez également une interface.
remarquez qu'il y a deux façons de considérer quand les utiliser. J'espère que cela t'aides.
la source
Je vois également beaucoup de bonnes réponses explicatives ici, donc je veux donner mon point de vue ici, y compris des informations supplémentaires sur ce que j'ai remarqué lors de l'utilisation de cette méthode.
Tests unitaires
Au cours des deux dernières années, j'ai écrit un projet de loisir et je n'ai pas fait de tests unitaires pour cela. Après avoir écrit environ 50 000 lignes, j'ai découvert qu'il serait vraiment nécessaire d'écrire des tests unitaires. Je n'ai pas utilisé d'interfaces (ou avec parcimonie) ... et quand j'ai fait mon premier test unitaire, j'ai découvert que c'était compliqué. Pourquoi?
Parce que je devais faire beaucoup d'instances de classe, utilisées pour l'entrée comme variables de classe et / ou paramètres. Les tests ressemblent donc plus à des tests d'intégration (devoir faire un «framework» complet de classes car tout était lié).
Peur des interfaces J'ai donc décidé d'utiliser des interfaces. Ma crainte était que je devais implémenter toutes les fonctionnalités partout (dans toutes les classes utilisées) plusieurs fois. D'une certaine manière, cela est vrai, cependant, en utilisant l'héritage, il peut être considérablement réduit.
Combinaison d'interfaces et d'héritage J'ai découvert que la combinaison est très bonne à utiliser. Je donne un exemple très simple.
De cette façon, la copie de code n'est pas nécessaire, tout en ayant l'avantage d'utiliser une voiture comme interface (ICar).
la source
Commençons par quelques définitions d'abord:
Interface n. L'ensemble de toutes les signatures définies par les opérations d'un objet est appelé l'interface de l'objet
Tapez n. Une interface particulière
Un exemple simple d'une interface de telle que définie ci - dessus serait toutes les méthodes d'objet PDO tels que
query()
,commit()
,close()
etc., dans son ensemble, et non séparément. Ces méthodes, c'est-à-dire son interface définissent l'ensemble complet des messages, requêtes qui peuvent être envoyées à l'objet.Un type tel que défini ci-dessus est une interface particulière. Je vais utiliser l'interface de forme maquillée pour démontrer:
draw()
,getArea()
,getPerimeter()
etc ..Si un objet est du type Base de données, nous voulons dire qu'il accepte les messages / requêtes de l'interface de base de données
query()
,commit()
etc. Les objets peuvent être de plusieurs types. Vous pouvez faire en sorte qu'un objet de base de données soit du type forme tant qu'il implémente son interface, auquel cas il s'agirait de sous-typage .De nombreux objets peuvent être de nombreuses interfaces / types différents et implémenter cette interface différemment. Cela nous permet de substituer des objets, nous permettant de choisir lequel utiliser. Également connu sous le nom de polymorphisme.
Le client ne connaîtra que l'interface et non l'implémentation.
Donc, en substance, la programmation d'une interface impliquerait de faire un certain type de classe abstraite comme
Shape
avec l'interface spécifiée uniquementdraw()
, c'est-à - diregetCoordinates()
,getArea()
etc. D'où un programme vers une interface et non une implémentation.la source
"Programmer vers l'interface" signifie ne pas fournir de code en dur, ce qui signifie que votre code doit être étendu sans casser la fonctionnalité précédente. Juste des extensions, pas la modification du code précédent.
la source