Que signifie "programmer vers une interface"?
j'ai vu cela mentionné plusieurs fois et je ne suis pas clair sur ce que cela signifie. Quand et pourquoi voudriez-vous faire cela?
je sais ce que font les interfaces, mais le fait que je ne sois pas clair là-dessus me fait penser que je manque de les utiliser correctement.
est-il juste si vous deviez faire:
IInterface classRef = new ObjectWhatever()
, Vous pouvez utiliser n'importe quelle classe qui implémente IInterface
? Quand devez-vous faire? La seule chose à laquelle je pense c'est si vous avez une méthode et vous n'êtes pas sûr de quel objet sera passé expect for it implementing IInterface
. Je ne sais pas combien de fois vous auriez besoin de faire ça... (D'ailleurs, comment pourriez-vous écrire une méthode qui prend un objet qui implémente une interface? Est-ce possible?)
désolé si j'ai complètement raté le but.
30 réponses
il y a des réponses merveilleuses ici à ces questions qui entrent dans toutes sortes de grands détails sur les interfaces et le code de couplage lâche, l'inversion du contrôle et ainsi de suite. Il y a des discussions assez houleuses, donc j'aimerais en profiter pour décomposer un peu les choses pour comprendre pourquoi une interface est utile.
quand j'ai commencé à être exposé aux interfaces, j'étais moi aussi confus au sujet de leur pertinence. Je ne comprends pas pourquoi vous besoin d'eux. Si nous utilisons un langage comme Java ou C#, nous avons déjà l'héritage et j'ai vu les interfaces comme une plus faible forme d'héritage et de pensée, " pourquoi s'embêter?"Dans un sens, j'avais raison, on peut penser aux interfaces comme une sorte de forme faible d'héritage, mais au-delà de cela, j'ai finalement compris leur utilisation comme une construction de langage en pensant à eux comme un moyen de classer les traits communs ou les comportements qui ont été exposés par potentiellement de nombreuses classes non liées de objet.
par exemple -- dites que vous avez un jeu SIM et que vous avez les classes suivantes:
class HouseFly inherits Insect {
void FlyAroundYourHead(){}
void LandOnThings(){}
}
class Telemarketer inherits Person {
void CallDuringDinner(){}
void ContinueTalkingWhenYouSayNo(){}
}
Clairement, ces deux objets n'ont rien en commun en termes d'héritage direct. Mais, vous pourriez dire qu'ils sont tous les deux ennuyeux.
disons que notre jeu doit avoir une sorte de hasard chose qui agace le joueur de jeu quand ils mangent le dîner. Cela pourrait être un HouseFly
ou un Telemarketer
ou les deux , mais comment autoriser les deux avec une seule fonction? Et comment demandez-vous à chaque type d'objet de "faire sa chose ennuyeuse" de la même manière?
la clé pour se rendre compte est que les deux Telemarketer
et HouseFly
partagent un comportement commun librement interprété même s'ils ne sont rien par rapport à la même chose en termes de modélisation. Alors, faisons une interface que les deux peuvent implémenter:
interface IPest {
void BeAnnoying();
}
class HouseFly inherits Insect implements IPest {
void FlyAroundYourHead(){}
void LandOnThings(){}
void BeAnnoying() {
FlyAroundYourHead();
LandOnThings();
}
}
class Telemarketer inherits Person implements IPest {
void CallDuringDinner(){}
void ContinueTalkingWhenYouSayNo(){}
void BeAnnoying() {
CallDuringDinner();
ContinueTalkingWhenYouSayNo();
}
}
Nous avons maintenant deux classes qui peuvent chacune être gênant dans leur propre chemin. 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 ont simplement besoin de satisfaire le contrat de IPest
-- ce contrat est simple. Il suffit de BeAnnoying
. À cet égard, nous pouvons modéliser ce qui suit:
class DiningRoom {
DiningRoom(Person[] diningPeople, IPest[] pests) { ... }
void ServeDinner() {
when diningPeople are eating,
foreach pest in pests
pest.BeAnnoying();
}
}
Ici, nous avons une salle à manger qui accepte un nombre de convives, et un certain nombre de ravageurs -- notez l'utilisation de l'interface. Cela signifie que dans notre petit monde, un membre du tableau pests
pourrait en fait être un objet Telemarketer
ou un objet HouseFly
.
la méthode ServeDinner
s'appelle lorsque le dîner est servi et que nos gens dans la salle à manger sont censés manger. Dans notre petit jeu, c'est là que nos parasites font leur travail -- chaque parasite est chargé d'être ennuyeux par l'intermédiaire de l'interface IPest
. De cette façon, nous pouvons facilement avoir à la fois Telemarketers
et HouseFlys
être ennuyeux à chacune de leurs façons -- nous nous nous soucions seulement que nous avons quelque chose dans l'objet DiningRoom
qui est un parasite, nous ne nous soucions pas vraiment de ce que c'est et ils pourraient n'avoir rien en commun avec les autres.
cet exemple de pseudo-code très artificiel (qui traînait sur beaucoup plus longtemps que je ne l'avais prévu) est simplement destiné à illustrer le genre de chose qui a finalement allumé la lumière pour moi en termes de quand nous pourrions utiliser une interface. Je m'excuse d'avance pour la bêtise de l'exemple, mais l'espoir qu'il contribue à votre compréhension. Et, pour être sûr, les autres réponses posté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.
L'exemple que j'ai utilisé pour donner aux étudiants est qu'ils devraient écrire
List myList = new ArrayList(); // programming to the List interface
au lieu de
ArrayList myList = new ArrayList(); // this is bad
Ces exactement le même aspect dans un programme court, mais si vous continuez d'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 les méthodes myList
qui sont définies par l'interface List
(donc pas de méthodes spécifiques ArrayList
). Si vous avez programmé à l'interface de cette façon, plus tard, vous pouvez décider que vous avez vraiment besoin
List myList = new TreeList();
et vous n'avez qu'à changer votre code dans un seul endroit. Vous savez déjà que le reste de votre code ne fait rien qui sera cassé en changeant le implementation parce que vous avez programmé sur le interface .
les avantages sont encore plus évidents (je pense) quand vous êtes parler des paramètres de la méthode et des valeurs de retour. Prenez par exemple:
public ArrayList doSomething(HashMap map);
cette déclaration de méthode vous lie à deux réalisations concrètes ( ArrayList
et HashMap
). Dès que cette méthode est appelée à partir d'un autre code, tout changement à ces types signifie probablement que vous allez devoir changer le code d'appel ainsi. Il serait préférable de programmer vers les interfaces.
public List doSomething(Map map);
maintenant il n'a pas d'importance quel genre de List
vous retournez, ou quel genre de Map
est passé comme paramètre. Les changements que vous effectuez à l'intérieur de la méthode doSomething
ne vous forceront pas à changer le code d'appel.
Programmer vers une interface c'est dire," j'ai besoin de cette fonctionnalité et je me fiche d'où elle vient."
envisager (en Java), l'interface List
versus les classes de béton ArrayList
et LinkedList
. Si tout ce qui m'importe est que j'ai une structure de données contenant plusieurs éléments de données auxquels je devrais accéder par itération, je choisirais un List
(et c'est 99% du temps). Si je sais que j'ai besoin de temps constant Insérer / Supprimer de l'une ou l'autre extrémité de la liste, je pourrais choisir l'implémentation concrète LinkedList
(ou plus probablement, utiliser l'interface file ). Si je sais que j'ai besoin d'un accès aléatoire par index, je choisirais la classe de béton ArrayList
.
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 veulent utiliser cette fonctionnalité à l'utiliser sans dépendre directement de votre classe d'implémentation. Si plus tard vous décidez de changer et d'utiliser une implémentation différente, vous n'avez qu'à changer la partie du code où l'implémentation est instancier. Le reste du code n'a pas besoin de changer car il dépend de l'interface, Pas de la classe implementing.
c'est très utile pour créer des tests unitaires. Dans la classe à l'essai vous l'avez dépend de l'interface et injectez une instance de l'interface dans la classe (ou une usine qui lui permet de construire des instances de l'interface si nécessaire) via le constructeur ou un settor 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 des données configurées dans votre test de l'unité. Vous pouvez le faire parce que votre classe sous test ne traite que de l'interface, Pas de votre implémentation concrète. Toute classe implémentant l'interface, y compris votre classe mock ou fake, fera l'affaire.
EDIT: ci-dessous est un lien vers un article où Erich Gamma discute de sa citation, "programme à une interface, pas une application."
vous devriez regarder en Inversion de commande:
- Martin Fowler: l'Inversion de Contrôle des Conteneurs et l'Injection de Dépendance modèle
- Wikipédia: l'Inversion de Contrôle
dans un tel scénario, vous n'écririez pas ceci:
IInterface classRef = new ObjectWhatever();
vous écririez quelque chose comme ceci:
IInterface classRef = container.Resolve<IInterface>();
cela entrerait dans une configuration basée sur des règles dans l'objet container
, et construirait l'objet réel pour vous, qui pourrait être ObjectWhatever. L'important est que vous puissiez remplacer cette règle par quelque chose qui utilise un autre type d'objet, et votre code fonctionnerait toujours.
si nous laissons le CIO en dehors de la table, vous pouvez écrire un code qui sait qu'il peut parler à un objet qui fait quelque chose de spécifique , mais pas quel type de objet ou comment il le fait.
cela serait pratique lors du passage des paramètres.
quant à votre question entre parenthèses "aussi, comment pourriez-vous écrire une méthode qui prend dans un objet qui implante une Interface? Est-ce possible?", dans C# vous utiliserez simplement le type d'interface pour le type de paramètre, comme ceci:
public void DoSomethingToAnObject(IInterface whatever) { ... }
cela se connecte directement dans le " parler à un objet qui fait quelque chose de spécifique."La méthode définie ci-dessus sait ce qu'il faut attendre de l'objet, qu'il implémente tout dans IInterface, mais il ne se soucie pas de quel type d'objet il s'agit, seulement qu'il adhère au contrat, ce qui est ce qu'est une interface.
par exemple, vous êtes probablement familier avec les calculatrices et vous en avez probablement utilisé plusieurs de votre temps, mais la plupart du temps, elles sont toutes différentes. Vous, en revanche, sait comment une calculatrice standard devrait fonctionner, de sorte que vous êtes en mesure de les utiliser tous, même si vous ne pouvez pas utiliser les caractéristiques spécifiques que chaque calculateur est qu'aucun des autres.
C'est la beauté des interfaces. Vous pouvez écrire un morceau de code, qui sait qu'il va obtenir des objets passés à lui qu'il peut attendre un certain comportement de. Il ne se soucie pas de savoir quel genre d'objet il est, seulement qu'il soutient le comportement nécessaire.
Permettez-moi de vous donner un exemple concret.
nous avons un système de traduction SUR MESURE Pour windows forme. Ce système effectue des boucles à travers les contrôles sur un formulaire et traduit le texte dans chacun. Le système sait comment gérer les contrôles de base, comme la propriété-type-of-control-that-has-a-Text -, et les choses de base similaires, mais pour tout ce qui est de base, il est insuffisant.
maintenant, puisque les contrôles héritent de classes prédéfinies sur lesquelles nous n'avons aucun contrôle, nous pourrions faire l'une des trois choses:
- construire un support pour notre système de traduction afin de détecter spécifiquement type de contrôle il travaille avec, et de traduire les bits corrects (cauchemar d'entretien)
- construire le support dans les classes de base (impossible, puisque tous les contrôles héritent de différentes classes prédéfinies)
- ajouter le support d'interface
Donc nous n'avons nr. 3. Tous nos contrôles mettent en œuvre Ilocalisable, qui est une interface qui nous donne une méthode, la capacité de traduire "lui-même" dans un conteneur de traduction texte/règles. En tant que tel, la forme n'a pas besoin de savoir quel type de contrôle elle a trouvé, seulement qu'elle implémente l'interface spécifique, et sait qu'il y a une méthode où elle peut appeler pour localiser le contrôle.
Programmer vers une interface n'a absolument rien à voir avec les interfaces abstraites comme On le voit en Java ou .NET. Ce n'est même pas un concept OOP.
ce que cela signifie vraiment est de ne pas aller jouer avec les internes d'un objet ou d'une structure de données. Utilisez L'interface de programmation 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 de l'accès au champ brut. Pour C, cela signifie utiliser des fonctions plutôt que des fonctions brutes. pointeur.
éditer: et avec des bases de données, cela signifie utiliser des vues et des procédures stockées au lieu de l'accès direct à la table.
Code à L'Interface L'implémentation N'a rien à voir avec Java, ni sa construction D'Interface.
ce concept a été mis en évidence dans le Patterns / Gang de quatre livres, mais était très probablement autour de bien avant cela. le concept existait certainement bien avant que Java n'existe.
la construction D'Interface Java a été créée pour faciliter cette idée (entre autres choses), et les gens sont devenus trop concentrés sur la construction comme le centre du sens plutôt que l'intention originale. Cependant, c'est la raison pour laquelle nous avons des méthodes et attributs publics et privés en Java, C++, C#, etc.
signifie simplement interagir avec l'interface publique d'un objet ou d'un système. Ne vous inquiétez pas ou même anticiper comment il fait ce qu'il fait à l'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 par opposition à privés. Nous sont destinés à utiliser les méthodes publiques parce que les méthodes privées ne sont là que pour une utilisation interne, au sein de la classe. Ils font de la mise en œuvre de la classe et peuvent être modifiés 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 la façon dont la classe fonctionne, sa mise en œuvre, sans casser comment les gens interagissent avec elle.
et vous pouvez programmer à l'interface, pas à l'implémentation sans jamais utiliser une construction D'Interface. vous pouvez programmer à l'interface pas l'implémentation en C++, qui n'a pas de construction D'Interface. Vous pouvez intégrer deux systèmes d'entreprise massive beaucoup plus robustement aussi longtemps qu'ils interagissent par des interfaces publiques (contrats) plutôt que d'appeler des méthodes sur les objets internes aux systèmes. Le on s'attend à ce que les interfaces réagissent toujours de la même façon, compte tenu des mêmes paramètres d'entrée; si elles sont mises en œuvre à l'interface et non à la mise en œuvre. Le concept fonctionne dans de nombreux endroits.
Secouer la pensée que les Interfaces de Java ont rien de ce que-si-jamais à faire avec la notion de "Programme de l'Interface, Pas la mise en Œuvre". Ils peuvent aider à appliquer le concept, mais ils sont pas le concept.
il semble que vous comprenez comment les interfaces fonctionnent mais ne savez pas quand les utiliser et quels avantages ils offrent. Voici quelques exemples de situations où une interface aurait du sens:
// if I want to add search capabilities to my application and support multiple search
// engines such as google, yahoo, live, etc.
interface ISearchProvider
{
string Search(string keywords);
}
alors je pourrais créer GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider etc.
// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
void Download(string url)
}
// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
Bitmap LoadImage(string filename)
}
puis créer JpegImageLoader, GifImageLoader, PngImageLoader,etc.
la plupart des add-ins et des systèmes de plugin ne fonctionnent pas interface.
une autre utilisation populaire est pour le modèle de dépôt. Dire que je veux charger une liste de codes postaux à partir de différentes sources
interface IZipCodeRepository
{
IList<ZipCode> GetZipCodes(string state);
}
alors je pourrais créer un XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository,etc. Pour mes applications web, Je crée souvent des dépôts XML très tôt afin de pouvoir démarrer quelque chose avant que la base de données Sql ne soit prête. Une fois la base de données prête, J'écris un SQLRepository pour remplacer le Version XML. Le reste de mon code reste inchangé puisqu'il exécute soley hors des interfaces.
Les méthodespeuvent accepter des interfaces telles que:
PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
{
Console.WriteLine(zipCode.ToString());
}
}
Il rend votre code beaucoup plus extensible et plus facile à maintenir, lorsque vous avez des ensembles de classes comparables. Je suis 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 dispositif médical. Nous sommes en train de développer une nouvelle version de cet appareil qui comporte de nouveaux composants que le client doit parfois configurer. Il y a deux types de nouvelles composantes, et ils sont différents, mais ils sont également très similaires. En gros, j'ai dû créer deux formes de configuration, deux listes de classes, 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 tiendrait presque tous de la logique, et puis les types dérivés de prendre soin des différences entre les deux composants. Cependant, les classes de base n'auraient pas été en mesure d'effectuer des opérations sur ces composants si je devais m'inquiéter types tout le temps (eh bien, ils auraient pu, mais il y aurait eu un "si" déclaration ou 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 changer quelque chose, il fonctionne partout et je n'ai pas de duplication de code.
si vous programmez en Java, JDBC en est un bon exemple. JDBC définit un ensemble d'interfaces mais ne dit rien sur l'implémentation. Vos applications peuvent être écrites en fonction de cet ensemble d'interfaces. En théorie, vous choisissez un pilote JDBC et votre application ne fera que fonctionner. Si vous découvrez qu'il y a 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 à faire de changement dans votre application, votre l'application fonctionne encore.
Programmer aux Interfaces est génial, il favorise l'accouplement lâche. Comme @lassevk l'a mentionné, L'Inversion du contrôle est une grande utilisation de cela.
en outre, regarder dans les principes solides . voici une série vidéo
il passe par un code dur (exemple fortement couplé) puis regarde les interfaces, finalement passer à un outil CIO/DI (NInject)
en plus de la réponse déjà sélectionnée (et les différents messages informatifs ici), je recommande fortement d'attraper une copie de tête premiers dessins . Il est très facile à lire et répondra directement à votre question, expliquera pourquoi il est important, et vous montrera de nombreux modèles de programmation que vous pouvez utiliser pour faire usage de ce principe (et d'autres).
beaucoup d'explications là-bas, mais pour le rendre encore plus simple.
Prenez par exemple un List
. On peut implémenter une liste avec as:
- un tableau interne
- Une liste liée
- autre mise en œuvre
en construisant à une interface, dites un List
. Vous ne cochez que la définition de la liste ou ce que List
signifie en réalité.
Vous pouvez utiliser n'importe quel type d'implémentation en interne par exemple une implémentation array
. Mais supposons que vous souhaitiez changer l'implémentation pour une raison quelconque, par exemple un bogue ou une performance. Il suffit alors de changer la déclaration List<String> ls = new ArrayList<String>()
en List<String> ls = new LinkedList<String>()
.
nulle part ailleurs dans le code, vous avez à changer quoi que ce soit d'autre; Car tout le reste a été construit sur la définition de List
.
je suis un tard venu à cette question, mais je tiens à mentionner ici que la ligne "Programme pour une interface, pas une mise en œuvre" a un bon sujet de discussion dans le GoF (Gang of four), les Modèles de Conception de livre.
, Il a déclaré, à la page. 18:
Programme pour une interface, pas une mise en œuvre
ne déclarez pas que les variables sont des instances de classes concrètes particulières. Au lieu de cela, engager uniquement pour une interface définie par une classe abstraite. Vous trouverez cela pour être un thème commun des modèles de conception dans ce livre.
et au-dessus de cela, il a commencé par:
il y a deux avantages à manipuler des objets uniquement en termes d'interface définie par des classes abstraites:
- les Clients ne sont pas au courant des types d'objets spécifiques qu'ils utilisent, tant que les objets adhèrent l'interface que les clients attendent.
- les Clients ne connaissent pas les classes qui implémentent ces objets. Les Clients ne connaissent que la(Les) Classe (S) abstraite (s) définissant l'interface.
donc, en d'autres termes, ne l'écrivez pas vos classes de sorte qu'il a une méthode quack()
pour les canards, puis une méthode bark()
pour les chiens, parce qu'ils sont trop spécifiques pour une mise en œuvre particulière d'une classe (ou sous-classe). Plutôt, écrivez la méthode en utilisant des noms qui sont assez généraux pour être utilisés dans la classe de base, tels que giveSound()
ou move()
, de sorte qu'ils peuvent être utilisés pour les canards, les chiens, ou même les voitures, et puis le client de vos classes peuvent juste dire .giveSound()
plutôt que de penser à savoir si utiliser quack()
ou bark()
ou même déterminer le type avant de délivrer le message correct à envoyer à l'objet.
pour ajouter aux postes existants, parfois le codage aux interfaces aide sur les grands projets lorsque les développeurs travaillent sur des composants séparés simultanément. Tout ce dont vous avez besoin est de définir les interfaces à l'avance et d'y écrire du code pendant que les autres développeurs écrivent du code à l'interface que vous mettez en œuvre.
Il est également 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
Donc, juste pour obtenir ce droit, l'avantage d'une interface, c'est que je peut séparer l'appel d'une méthode d'une classe. Au lieu de créer une instance de l'interface, où l'implémentation est donnée à partir de n'importe quelle classe que je choisis qui implémente cette interface. Cela me permet 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 quel objet il est.
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 passé et dit de bouger. Sans la méthode chaque connaître le type de classe il est.
Imaginez que vous ayez un produit appelé "Zebra" qui peut être étendu par des plugins. Il trouve les plugins en cherchant DLLs dans un répertoire. Il charge tous ces DLLs 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 - il ne se soucie pas de ce que sont les classes. Il importe seulement qu'ils remplissent le spécification de l'interface.
Les Interfacessont un moyen de définir des points d'extensibilité comme celui-ci. Code qui parle à une interface est plus faiblement couplés - en fait, il n'est pas couplé à un autre code. Il peut interagir avec des plugins écrits des années plus tard par des personnes qui n'ont jamais rencontré le développeur original.
vous pouvez à la place utiliser une classe de base avec des fonctions virtuelles - tous les plugins seraient dérivés de la classe de base. Mais c'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.
C++ explication.
pensez à une interface comme aux méthodes publiques de vos classes.
vous pourriez alors créer un modèle qui "dépend" de ces méthodes publiques afin d'effectuer sa propre fonction (il fait des appels de fonction définis dans les classes interface publique). Disons que ce modèle est un conteneur, comme une classe vectorielle, et l'interface dont il dépend est un algorithme de recherche.
toute classe d'algorithme qui définit functions / Vector interface fait des appels pour satisfaire le 'contrat' (comme quelqu'un a expliqué dans la réponse originale). 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 pourriez fournir n'importe quel algorithme de recherche différent/classe aussi longtemps qu'il a fourni l'interface que le vecteur dépend de (recherche de bulle, recherche séquentielle, recherche rapide).
vous pourriez également vouloir concevoir d'autres conteneurs (listes, Files d'attente) qui exploiteraient le même algorithme de recherche que vecteur en leur faisant remplir l'interface/contrat que vos algorithmes de recherche dépend.
cela permet de gagner du temps (Principe OOP 'code reuse') car vous êtes en mesure d'écrire un algorithme une fois au lieu de Encore et encore et encore spécifiques à chaque nouvel objet que vous créez sans compliquer le problème avec un arbre héréditaire envahi par la végétation.
comme pour "manquer" sur la façon dont les choses fonctionnent; big-time (au moins en C++), comme c'est la façon dont la plupart du cadre de la bibliothèque de modèles standard fonctionne.
bien sûr lors de l'utilisation de l'héritage et des classes abstraites la méthodologie de programmation À une interface change; mais le principe est le même, vos fonctions/méthodes publiques sont votre interface de classes.
C'est un vaste sujet et un de la principes fondamentaux des modèles de conception.
en Java, ces classes concrètes implémentent toutes L'interface CharSequence:
CharBuffer, String, StringBuffer, StringBuilder
ces classes concrètes n'ont pas de classe mère commune autre que L'objet, de sorte qu'il n'y a rien qui les relie, si ce n'est le fait qu'elles ont chacune quelque chose à voir avec des tableaux de caractères, représentant tel ou manipulant tel. Par exemple, les caractères de la Chaîne ne peut pas être modifié une fois qu'un objet String est instancié, alors que les caractères de StringBuffer ou StringBuilder peuvent être édités.
pourtant chacune de ces classes est capable de mettre en œuvre convenablement les méthodes de L'interface CharSequence:
char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()
dans certains cas, les classes de bibliothèque de classe Java qui acceptaient la chaîne ont été révisées pour accepter maintenant L'interface CharSequence. Donc si vous avez une instance de StringBuilder, au lieu d'extraire un String object (qui signifie instanciation d'une nouvelle instance d'objet), peut simplement passer le StringBuilder lui-même comme il implémente 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 sous-jacente de l'objet de classe concret. Toutes ces classes de béton mettent en œuvre l'interface annexe:
BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, OutputStreamWriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, StringWriter, Writer
en termes simples... Si j'écris un nouveau nageur de classe pour ajouter la fonctionnalité swim () et que j'ai besoin d'utiliser un objet de classe say Dog, et que cette classe Dog implémente l'interface Animal qui déclare swim () [pour mieux comprendre...vous pouvez dessiner un diagramme de ce dont je parle]. Au sommet de la hiérarchie (Animal) c'est très abstrait alors qu'au bas (chien) c'est très concret. La façon dont je pense à "la programmation aux interfaces" est que, comme J'écris Swimmer classe, je veux écrire mon code contre l'interface qui est aussi haut que cette hiérarchie qui dans ce cas est objet Animal. Une interface est libre de détails d'implémentation et rend ainsi votre code librement couplé. Les détails de la mise en œuvre peuvent être changés avec le temps, mais cela n'affecterait pas le code restant puisque tout ce que vous interagissez est avec l'interface et pas la mise en œuvre. Vous ne vous souciez pas de ce que l'implémentation est comme...tout ce que vous savez qu'il y aura une classe qui serait implémenter l'interface.
nouvelle: Postman est invité à rentrer à la maison par la maison et de recevoir les couvertures contient (lettres,documents,chèques,carte-cadeau,demande,loveletter) avec adresse écrite sur elle à livrer.
supposez qu'il n'y a pas de couverture et demandez à post man de rentrer chez lui et de recevoir toutes les choses et de livrer à une autre personne le facteur peut être confus,
tellement mieux l'envelopper avec couvercle(dans notre histoire, il est l'interface) puis il fera son travail très bien.
maintenant Postman job doit recevoir et livrer les couvertures seulement..(il n'gêné de ce qui est à l'intérieur de la couverture).
créer le type de interface
pas le type réel, mais mettre en œuvre avec le type réel.
créer à l'interface signifie que vos composants obtenir correspond au reste du code facilement
je vous donne l'exemple.
vous avez l'interface de L'avion comme ci-dessous.
interface Airplane{
parkPlane();
servicePlane();
}
supposons que vous ayez des méthodes dans votre classe de contrôleur D'avions comme
parkPlane(Airplane plane)
et
servicePlane(Airplane plane)
implémenté dans votre programme. Il ne sera pas briser votre code.
Je veux dire, il n'a pas besoin de changer aussi longtemps qu'il accepte les arguments comme AirPlane
.
parce qu'il acceptera n'importe quel avion malgré le 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 l'implémente, donc
public class Fighter implements Airplane {
public void parkPlane(){
// Specific implementations for fighter plane to park
}
public void servicePlane(){
// Specific implementatoins for fighter plane to service.
}
}
la même chose pour HighFlyer et autre classe:
public class HighFlyer implements Airplane {
public void parkPlane(){
// Specific implementations for HighFlyer plane to park
}
public void servicePlane(){
// specific implementatoins for HighFlyer plane to service.
}
}
pensez maintenant à votre contrôleur classes utilisant AirPlane
plusieurs fois,
supposons que votre classe de Controller est Controllplane comme ci-dessous,
public Class ControlPlane{
AirPlane plane;
// so much method with AirPlane reference are used here...
}
ici, la magie opère comme
vous pouvez faire vos nouvelles instances de type AirPlane
autant que vous voulez et vous ne changerez pas""
code de la classe ControlPlane
.
vous pouvez ajouter instance..
JumboJetPlane // implementing AirPlane interface.
AirBus // implementing AirPlane interface.
, vous pouvez supprimer instance.. des types précédemment créés aussi.
Q: -... "Vous pouvez utiliser n'importe quelle classe qui implémente l'interface?"
oui.Q: -... "Quand devez-vous faire?"
R: - Chaque fois que vous avez besoin d'une classe qui implémente l'interface(s).
Note: nous ne pouvions pas instancier une interface non implémentée par une classe - True.
- pourquoi?
- parce que interface n'a que des prototypes de méthodes, pas de définitions (juste des noms de fonctions, pas leur logique)
AnIntf anInst = new Aclass ();
// nous pourrions le faire seulement si Classe implémente AnIntf.
// anInst aura une référence Glass.
Note::
maintenant nous pouvons comprendre ce qui se passe si Bclass et Cclass implémentent le même Dintf.
Dintf bInst = new Bclass();
// now we could call all Dintf functions implemented (defined) in Bclass.
Dintf cInst = new Cclass();
// now we could call all Dintf functions implemented (defined) in Cclass.
Ce que nous avons:
mêmes prototypes d'interface (noms de fonctions dans l'interface), et appellent des implémentations différentes.
Bibliographie:
Prototypes-Wikipédia
Interface est comme le contrat où vous voulez que votre classe de mise en œuvre pour mettre en œuvre des méthodes écrites dans le contrat(Interface).Puisque java ne fournit pas d'héritage multiple,la programmation À l'interface est un bon moyen d'atteindre le but de l'héritage multiple.Si vous avez une classe A qui étend déjà une autre Classe B mais vous voulez que la classe A devrait également suivre certaines lignes directrices ou mettre en œuvre un certain contrat, alors vous pouvez le faire en programmant à la stratégie d'interface.
il peut être avantageux de programmer vers des interfaces, même si nous ne dépendons pas d'abstractions.
Programmation d'interfaces nous oblige à utiliser un contexte sous-ensemble d'un objet. qui aide parce qu'il:
- nous empêche de faire des choses contextuellement inappropriées, et
- nous permet de changer en toute sécurité la mise en œuvre à l'avenir.
par exemple, considérez une classe Person
qui implémente l'interface Friend
et Employee
.
class Person implements AbstractEmployee, AbstractFriend {
}
dans le contexte de l'anniversaire de la personne, nous programmons à l'interface Friend
, pour éviter de traiter la personne comme un Employee
.
function party() {
const friend: Friend = new Person("Kathryn");
friend.HaveFun();
}
dans le contexte du travail de la personne, nous programmons l'interface Employee
pour éviter de brouiller les limites du milieu de travail.
function workplace() {
const employee: Employee = new Person("Kathryn");
employee.DoWork();
}
Super. Nous nous sommes comportés de manière appropriée dans différents contextes, et notre logiciel fonctionne bien.
loin dans le futur, si notre entreprise change pour travailler avec des chiens, nous pouvons changer le logiciel assez facilement. Tout d'abord, nous créons la classe Dog
qui implémente à la fois Friend
et Employee
. Ensuite, nous changeons en toute sécurité new Person()
en new Dog()
. Même si les deux fonctions ont des milliers de lignes de code, Cette édition simple fonctionnera parce que nous savons que ce qui suit est vrai:
- fonction
party
utilise uniquement leFriend
sous-ensemble dePerson
. - Fonction
workplace
n'utilise que leEmployee
sous-ensemble dePerson
. - Classe
Dog
implémente les interfacesFriend
etEmployee
.
, d'autre part, si party
ou workplace
, ont programmés contre Person
, il y aurait un risque que les deux aient un code spécifique Person
. Le passage de Person
à Dog
nous obligerait à passer au peigne fin le code pour faire disparaître tout code spécifique Person
que Dog
ne supporte pas.
the moral : la programmation aux interfaces aide notre code à se comporter correctement et à être prêt pour le changement. Il prépare également notre code à dépendre d'abstractions, ce qui apporte encore plus d'avantages.
aussi je vois beaucoup de bonnes et des réponses explicatives ici, donc je veux donner mon point de vue ici, y compris quelques informations supplémentaires ce que j'ai remarqué en utilisant cette méthode.
tests Unitaires
pendant les deux dernières années, j'ai écrit un projet de passe-temps et je n'ai pas écrit des tests unitaires pour elle. Après avoir écrit environ 50K lignes j'ai découvert qu'il serait vraiment nécessaire d'écrire des tests unitaires. Je n'ai pas utilisé d'interfaces (ou très avec parcimonie) ... et quand j'ai fait mon premier test, j'ai découvert que c'était compliqué. Pourquoi?
parce que j'ai dû faire beaucoup d'instances de classe, utilisées pour l'entrée comme variables de classe et/ou paramètres. Les tests ressemblent donc davantage à des tests d'intégration (devant faire un "cadre" complet de classes puisque tout était lié).
peur des interfaces J'ai donc décidé d'utiliser des interfaces. Ma crainte était que je devais mettre en œuvre tous fonctionnalité partout (dans toutes les classes utilisées) plusieurs fois. D'une certaine façon, cela est vrai, mais 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.
public interface IPricable
{
int Price { get; }
}
public interface ICar : IPricable
public abstract class Article
{
public int Price { get { return ... } }
}
public class Car : Article, ICar
{
// Price does not need to be defined here
}
de cette façon, copier le code n'est pas nécessaire, tout en ayant l'avantage d'utiliser une voiture comme interface (ICar).
commençons par quelques définitions:
Interface N. l'ensemble de toutes les signatures définies par les opérations d'un objet est appelé l'interface avec l'objet
Type N. une interface particulière
un exemple simple de interface comme défini ci-dessus serait toutes les méthodes D'objet AOP telles que: query()
, commit()
, close()
etc., dans son ensemble, pas séparément. Ces méthodes, c'est-à-dire son interface définissent l'ensemble complet des messages, des requêtes qui peuvent être envoyées à l'objet.
A type comme défini ci-dessus est une interface particulière. Je vais utiliser l'interface Shape pour démontrer: draw()
, getArea()
, getPerimeter()
etc..
si un objet est du type de 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 avoir un objet de base de données du type shape aussi longtemps qu'il implémente son interface, auquel cas ce serait sous-typage .
de nombreux objets peuvent être de plusieurs interfaces/types différents et mettre en œuvre cette interface différemment. Cela permet nous devons substituer des objets, nous laissant choisir lequel utiliser. Aussi connu comme le polymorphisme.
le client ne connaîtra que l'interface et non l'implémentation.
donc, en essence, programmer vers une interface impliquerait de faire un certain type de classe abstraite comme Shape
avec l'interface seulement spécifié i.e. draw()
, getCoordinates()
, getArea()
etc.. Et puis avoir différentes classes concrètes mettre en œuvre ces interfaces comme une classe de cercle, de carré, de Triangle. donc programmer vers une interface pas une implémentation.
programme à une interface permet de changer la mise en œuvre du contrat défini par l'interface de façon transparente. Il permet le couplage lâche entre le contrat et les implémentations spécifiques.
IInterface classRef = new ObjectWhatever()
vous pouvez utiliser n'importe quelle classe qui implémente IInterface? Quand devez-vous faire?
Regardez cette question pour un bon exemple.
pourquoi préférer l'interface pour une classe Java?
est-ce que l'utilisation d'une Interface atteint la performance?
si oui, combien?
Oui. Il aura de légères performances au-dessus en moins de secondes. Mais si votre application a besoin de changer la mise en œuvre de l'interface dynamiquement, ne vous inquiétez pas de l'impact sur les performances.
Comment Pouvez-vous l'éviter sans avoir à maintenir deux bits de code?
N'essayez pas d'éviter les implémentations multiples 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 passer d'une implémentation à une autre implémentation.
Un bon cas d'utilisation: la mise en Œuvre de la Stratégie motif:
Je ne retiens pas interface
s sont la chose la plus importante dans une langue: il est plus couramment utilisé la classe héritant. Mais de toute façon, elles sont importantes!
par exemple (c'est le code Java
, mais il peut simplement adapté à C#
ou beaucoup d'autres langues):
interface Convertable<T> {
T convert();
}
public class NumerableText implements Convertable<Integer> {
private String text = "";
public NumerableText() { }
public NumerableText(String text) {
this.text = text;
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public Integer convert() {
return this.text.hashCode();
}
}
public class NumerableTextArray implements Convertable<Integer> {
private String[] textArray = "";
public NumerableTextArray() { }
public NumerableTextArray(String[] textArray) {
this.textArray = textArray;
}
public String[] getTextArray() {
return this.textArray;
}
public void setTextArray(String[] text) {
this.textArray = textArray;
}
public Integer convert() {
Integer value = 0;
for (String text : textArray)
value += text.hashCode();
return value;
}
}
public class Foo {
public static void main() {
Convertable<Integer> num1 = new NumerableText("hello");
Convertable<Integer> num2 = new NumerableTextArray(new String[] { "test n°1", "test n°2" });
System.out.println(String.valueOf(num1.convert()));
System.out.println(String.valueOf(num2.convert()));
//Here are you two numbers generated from two classes of different type, but both with the method convert(), which allows you to get that number.
}
}
programme à l'interface signifie Ne pas fournir des codes durs de la manière, signifie que vos codes doivent être étendus sans briser la fonctionnalité précédente..... juste extensions de ne pas éditer le précédent codes