La raison des interfaces m'échappe vraiment. D'après ce que je comprends, c'est une sorte de contournement pour l'héritage multiple inexistant qui n'existe pas en C # (ou du moins m'a-t-on dit).
Tout ce que je vois, c'est que vous prédéfinissez certains membres et certaines fonctions, qui doivent ensuite être redéfinis dans la classe. Rendant ainsi l'interface redondante. Cela ressemble à de la syntaxe… eh bien, de la camelote pour moi (je vous en prie, aucune infraction ne voulait dire.
Dans l'exemple donné ci-dessous tiré d'un thread d'interfaces C # différent en cas de débordement de pile, je créerais simplement une classe de base appelée Pizza au lieu d'une interface.
exemple simple (tiré d'une contribution de débordement de pile différente)
public interface IPizza
{
public void Order();
}
public class PepperoniPizza : IPizza
{
public void Order()
{
//Order Pepperoni pizza
}
}
public class HawaiiPizza : IPizza
{
public void Order()
{
//Order HawaiiPizza
}
}
struct
types.Réponses:
Le fait est que l'interface représente un contrat . Un ensemble de méthodes publiques que toute classe d'implémentation doit posséder. Techniquement, l'interface ne régit que la syntaxe, c'est-à-dire quelles méthodes sont là, quels arguments elles obtiennent et ce qu'elles renvoient. Habituellement, ils encapsulent également la sémantique, bien que cela uniquement par la documentation.
Vous pouvez alors avoir différentes implémentations d'une interface et les échanger à volonté. Dans votre exemple, puisque chaque instance de pizza est un,
IPizza
vous pouvez l'utiliserIPizza
partout où vous gérez une instance d'un type de pizza inconnu. Toute instance dont le type hériteIPizza
est garantie d'être ordonnable, car elle a uneOrder()
méthode.Python n'est pas typé statiquement, donc les types sont conservés et recherchés lors de l'exécution. Vous pouvez donc essayer d'appeler une
Order()
méthode sur n'importe quel objet. Le runtime est heureux tant que l'objet a une telle méthode et probablement juste hausse les épaules et dit »Meh.« Si ce n'est pas le cas. Ce n'est pas le cas en C #. Le compilateur est responsable de faire les appels corrects et s'il a juste un peu aléatoire,object
le compilateur ne sait pas encore si l'instance pendant l'exécution aura cette méthode. Du point de vue du compilateur, il n'est pas valide car il ne peut pas le vérifier. (Vous pouvez faire de telles choses avec la réflexion ou ledynamic
mot - clé, mais cela va un peu loin en ce moment, je suppose.)Notez également qu'une interface au sens habituel ne doit pas nécessairement être un C #
interface
, il peut également s'agir d'une classe abstraite ou même d'une classe normale (ce qui peut être utile si toutes les sous-classes doivent partager du code commun - dans la plupart des cas , cependant,interface
suffit).la source
foo
implémentationsIWidget
, un programmeur qui voit un appel àfoo.woozle()
peut consulter la documentationIWidget
et savoir ce que cette méthode est censée faire . Le programmeur n'a peut-être aucun moyen de savoir d'où proviendra le code de l'implémentation réelle, mais tout type respectant leIWidget
contrat d'interface sera implémentéfoo
d'une manière cohérente avec ce contrat. Dans un langage dynamique, en revanche, il n'y aurait pas de point de référence clair pour ce quifoo.woozle()
devrait signifier.Personne n'a vraiment expliqué en termes clairs comment les interfaces sont utiles, donc je vais essayer (et voler un peu la réponse de Shamim).
Prenons l'idée d'un service de commande de pizza. Vous pouvez avoir plusieurs types de pizzas et une action commune pour chaque pizza prépare la commande dans le système. Chaque pizza doit être préparée mais chaque pizza est préparée différemment . Par exemple, lorsqu'une pizza à croûte farcie est commandée, le système doit probablement vérifier que certains ingrédients sont disponibles au restaurant et mettre de côté ceux qui ne sont pas nécessaires pour les pizzas à plats profonds.
Lors de l'écriture dans le code, techniquement, vous pouvez simplement faire
Cependant, les pizzas à vaisselle profonde (en termes C #) peuvent nécessiter des propriétés différentes à définir dans la
Prepare()
méthode que la croûte farcie, et donc vous vous retrouvez avec beaucoup de propriétés facultatives, et la classe ne se met pas à l'échelle correctement (que faire si vous ajoutez de nouvelles types de pizza).La bonne façon de résoudre ce problème est d'utiliser l'interface. L'interface déclare que toutes les pizzas peuvent être préparées, mais chaque pizza peut être préparée différemment. Donc, si vous avez les interfaces suivantes:
Maintenant, votre code de traitement des commandes n'a pas besoin de savoir exactement quels types de pizzas ont été commandés pour gérer les ingrédients. Il a juste:
Même si chaque type de pizza est préparé différemment, cette partie du code n'a pas à se soucier du type de pizza que nous traitons, il sait juste qu'il est appelé pour des pizzas et donc chaque appel à
Prepare
préparera automatiquement chaque pizza correctement en fonction de son type, même si la collection comprend plusieurs types de pizzas.la source
public
. Donc, à l'intérieur de l'interface, cela devrait être justevoid Prepare();
Pour moi, au début, l'intérêt de ceux-ci n'est devenu clair que lorsque vous cessez de les regarder comme des choses pour rendre votre code plus facile / plus rapide à écrire - ce n'est pas leur but. Ils ont un certain nombre d'utilisations:
(Cela va perdre l'analogie avec la pizza, car il n'est pas très facile de visualiser une utilisation de cela)
Supposons que vous créez un jeu simple à l'écran et qu'il aura des créatures avec lesquelles vous interagirez.
R: Ils peuvent faciliter la maintenance de votre code à l'avenir en introduisant un couplage lâche entre votre implémentation frontale et votre implémentation back end.
Vous pourriez écrire ceci pour commencer, car il n'y aura que des trolls:
L'extrémité avant:
Deux semaines plus tard, le marketing décide que vous avez également besoin d'Orcs, comme ils en lisent sur Twitter, vous devez donc faire quelque chose comme:
L'extrémité avant:
Et vous pouvez voir comment cela commence à devenir désordonné. Vous pouvez utiliser une interface ici pour que votre front-end soit écrit une fois et (voici le bit important) testé, et vous pouvez ensuite brancher d'autres éléments back-end si nécessaire:
L'avant est alors:
Le frontal ne se soucie désormais que de l'interface ICreature - il ne se soucie pas de l'implémentation interne d'un troll ou d'un orc, mais uniquement du fait qu'ils implémentent ICreature.
Un point important à noter lorsque l'on regarde cela de ce point de vue est que vous auriez également pu facilement utiliser une classe de créature abstraite, et de ce point de vue, cela a le même effet.
Et vous pouvez extraire la création dans une usine:
Et notre front-end deviendrait alors:
Désormais, le front-end n'a même plus besoin de faire référence à la bibliothèque où Troll et Orc sont implémentés (à condition que l'usine soit dans une bibliothèque séparée) - il n'a besoin de rien savoir à leur sujet.
B: Supposons que vous ayez des fonctionnalités que seules certaines créatures auront dans votre structure de données par ailleurs homogène , par exemple
Le frontal pourrait alors être:
C: Utilisation pour l'injection de dépendances
La plupart des frameworks d'injection de dépendances sont plus faciles à utiliser lorsqu'il y a un couplage très lâche entre le code frontal et la mise en œuvre du serveur principal. Si nous prenons notre exemple d'usine ci-dessus et que notre usine implémente une interface:
Notre frontal pourrait alors avoir injecté ceci (par exemple un contrôleur API MVC) via le constructeur (généralement):
Avec notre framework DI (par exemple Ninject ou Autofac), nous pouvons les configurer de telle sorte qu'au moment de l'exécution, une instance de CreatureFactory sera créée chaque fois qu'un ICreatureFactory est nécessaire dans un constructeur - cela rend notre code agréable et simple.
Cela signifie également que lorsque nous écrivons un test unitaire pour notre contrôleur, nous pouvons fournir une ICreatureFactory simulée (par exemple, si l'implémentation concrète nécessite un accès DB, nous ne voulons pas que nos tests unitaires en dépendent) et tester facilement le code dans notre contrôleur .
D: Il existe d'autres utilisations, par exemple, vous avez deux projets A et B qui, pour des raisons «héritées», ne sont pas bien structurés, et A fait référence à B.
Vous trouverez ensuite des fonctionnalités dans B qui doivent appeler une méthode déjà dans A. Vous ne pouvez pas le faire en utilisant des implémentations concrètes car vous obtenez une référence circulaire.
Vous pouvez avoir une interface déclarée en B que la classe de A implémente ensuite. Votre méthode en B peut passer une instance d'une classe qui implémente l'interface sans problème, même si l'objet concret est d'un type en A.
la source
Voici vos exemples ré-expliqués:
la source
Les exemples ci-dessus n'ont pas beaucoup de sens. Vous pouvez accomplir tous les exemples ci-dessus en utilisant des classes (classe abstraite si vous voulez qu'elle se comporte uniquement comme un contrat ):
Vous obtenez le même comportement qu'avec l'interface. Vous pouvez créer un
List<Food>
et itérer sans savoir quelle classe se trouve en haut.Un exemple plus adéquat serait l'héritage multiple:
Ensuite, vous pouvez les utiliser tous
MenuItem
et ne vous souciez pas de la façon dont ils gèrent chaque appel de méthode.la source
Explication simple avec analogie
Le problème à résoudre: quel est le but du polymorphisme?
Analogie: Je suis donc un contremaître sur un chantier de construction.
Des commerçants marchent tout le temps sur le chantier. Je ne sais pas qui va franchir ces portes. Mais je leur dis essentiellement quoi faire.
Le problème avec l'approche ci-dessus est que je dois: (i) savoir qui marche dans cette porte, et selon qui c'est, je dois leur dire quoi faire. Cela signifie que je dois tout savoir sur un métier particulier. Il y a des coûts / avantages associés à cette approche:
Les implications de savoir quoi faire:
Cela signifie que si le code du charpentier passe de:
BuildScaffolding()
àBuildScaffold()
(c'est- à -dire un léger changement de nom), jeForeperson
devrai également changer la classe appelante (c'est-à-dire la classe) également - vous devrez apporter deux modifications au code au lieu de (essentiellement ) juste un. Avec le polymorphisme, vous n'avez (fondamentalement) qu'à effectuer un seul changement pour obtenir le même résultat.Deuxièmement, vous n'aurez pas à demander constamment: qui êtes-vous? ok fais ça ... qui es-tu? ok fais ça ..... polymorphisme - il SÈCHE ce code, et est très efficace dans certaines situations:
avec le polymorphisme, vous pouvez facilement ajouter des classes supplémentaires de gens de métier sans modifier le code existant. (c'est-à-dire le deuxième des principes de conception SOLIDE: principe d'ouverture-fermeture).
La solution
Imaginez un scénario où, peu importe qui passe la porte, je peux dire: "Work ()" et ils font leur travail de respect dans lequel ils se spécialisent: le plombier s'occupe des tuyaux et l'électricien s'occupe des fils.
L'avantage de cette approche est que: (i) je n'ai pas besoin de savoir exactement qui entre par cette porte - tout ce que je dois savoir, c'est qu'ils seront un type de métier et qu'ils peuvent faire du travail, et d'autre part , (ii) je n'ai pas besoin de savoir quoi que ce soit sur ce commerce particulier. Le tradie s'en chargera.
Donc au lieu de ça:
Je peux faire quelque chose comme ça:
Quel est l'avantage?
L'avantage est que si les exigences spécifiques du charpentier, etc. changent, le contremaître n'aura pas besoin de changer son code - il n'a pas besoin de savoir ou de se soucier. Tout ce qui compte, c'est que le menuisier sache ce que veut dire Work (). Deuxièmement, si un nouveau type de travailleur de la construction vient sur le chantier, le contremaître n'a pas besoin de savoir quoi que ce soit sur le métier - tout le contremaître se soucie si le travailleur de la construction (.eg soudeur, vitrier, carreleur, etc.) peut faire travailler ().
Problème et solution illustrés (avec et sans interfaces):
Pas d'interface (exemple 1):
Pas d'interface (exemple 2):
Avec une interface:
Résumé
Une interface vous permet de demander à la personne d'effectuer le travail qui lui est assigné, sans que vous ayez la connaissance exacte de qui elle est ni des détails de ce qu'elle peut faire. Cela vous permet d'ajouter facilement de nouveaux types (de commerce) sans changer votre code existant (eh bien techniquement, vous le changez un tout petit peu), et c'est le véritable avantage d'une approche OOP par rapport à une méthodologie de programmation plus fonctionnelle.
Si vous ne comprenez rien de ce qui précède ou si ce n'est pas clair, demandez dans un commentaire et j'essaierai d'améliorer la réponse.
la source
En l'absence de typage canard comme vous pouvez l'utiliser en Python, C # s'appuie sur des interfaces pour fournir des abstractions. Si les dépendances d'une classe étaient tous des types concrets, vous ne pourriez pas passer dans un autre type - en utilisant des interfaces, vous pouvez passer dans n'importe quel type qui implémente l'interface.
la source
L'exemple Pizza est mauvais car vous devez utiliser une classe abstraite qui gère la commande et les pizzas doivent simplement remplacer le type de pizza, par exemple.
Vous utilisez des interfaces lorsque vous avez une propriété partagée, mais vos classes héritent de différents endroits, ou lorsque vous n'avez pas de code commun que vous pourriez utiliser. Par exemple, il s'agit de choses qui peuvent être éliminées
IDisposable
, vous savez qu'elles seront éliminées, vous ne savez tout simplement pas ce qui se passera lorsqu'elles seront éliminées.Une interface est juste un contrat qui vous indique certaines choses qu'un objet peut faire, quels paramètres et quels types de retour attendre.
la source
Considérez le cas où vous ne contrôlez pas ou ne possédez pas les classes de base.
Prenez les contrôles visuels par exemple, dans .NET for Winforms, ils héritent tous de la classe de base Control, qui est complètement définie dans le framework .NET.
Supposons que vous soyez en train de créer des contrôles personnalisés. Vous souhaitez créer de nouveaux boutons, zones de texte, listes de visualisation, grilles, etc., et vous souhaitez qu'ils aient tous certaines fonctionnalités propres à votre ensemble de contrôles.
Par exemple, vous souhaiterez peut-être une façon commune de gérer les thèmes ou une façon commune de gérer la localisation.
Dans ce cas, vous ne pouvez pas "simplement créer une classe de base" car si vous le faites, vous devez réimplémenter tout ce qui concerne les contrôles.
Au lieu de cela, vous descendez de Button, TextBox, ListView, GridView, etc. et ajoutez votre code.
Mais cela pose un problème, comment pouvez-vous maintenant identifier quels contrôles sont "à vous", comment pouvez-vous créer du code qui dit "pour tous les contrôles du formulaire qui sont les miens, définissez le thème sur X".
Entrez les interfaces.
Les interfaces sont un moyen de regarder un objet, de déterminer que l'objet adhère à un certain contrat.
Vous devez créer "YourButton", descendre de Button et ajouter la prise en charge de toutes les interfaces dont vous avez besoin.
Cela vous permettrait d'écrire du code comme celui-ci:
Cela ne serait pas possible sans interfaces, à la place, vous devriez écrire du code comme ceci:
la source
Dans ce cas, vous pourriez (et le feriez probablement) simplement définir une classe de base Pizza et en hériter. Cependant, il y a deux raisons pour lesquelles les interfaces vous permettent de faire des choses qui ne peuvent pas être réalisées autrement:
Une classe peut implémenter plusieurs interfaces. Il définit simplement les fonctionnalités que la classe doit avoir. L'implémentation d'une gamme d'interfaces signifie qu'une classe peut remplir plusieurs fonctions à différents endroits.
Une interface peut être définie dans une portée plus étendue que la classe ou l'appelant. Cela signifie que vous pouvez séparer la fonctionnalité, séparer la dépendance du projet et conserver la fonctionnalité dans un projet ou une classe, et l'implémentation de cela ailleurs.
L'une des implications de 2 est que vous pouvez modifier la classe utilisée, en exigeant simplement qu'elle implémente l'interface appropriée.
la source
Considérez que vous ne pouvez pas utiliser l'héritage multiple en C #, puis réexaminez votre question.
la source
Interface = contrat, utilisé pour un couplage lâche (voir GRASP ).
la source
Si je travaille sur une API pour dessiner des formes, je peux vouloir utiliser DirectX ou des appels graphiques, ou OpenGL. Donc, je vais créer une interface, qui résumera mon implémentation de ce que vous appelez.
Donc , vous appelez une méthode d'usine:
MyInterface i = MyGraphics.getInstance()
. Ensuite, vous avez un contrat, vous savez donc à quelles fonctions vous pouvez vous attendreMyInterface
. Ainsi, vous pouvez appeleri.drawRectangle
oui.drawCube
et savoir que si vous échangez une bibliothèque pour une autre, les fonctions sont prises en charge.Cela devient plus important si vous utilisez l'injection de dépendances, car vous pouvez alors, dans un fichier XML, échanger les implémentations.
Ainsi, vous pouvez avoir une bibliothèque de chiffrement qui peut être exportée à usage général et une autre qui est à vendre uniquement aux entreprises américaines, et la différence est que vous modifiez un fichier de configuration, et le reste du programme n'est pas modifié.
Cela est beaucoup utilisé avec les collections en .NET, car vous devez simplement utiliser, par exemple, des
List
variables, et ne vous inquiétez pas s'il s'agissait d'une ArrayList ou d'une LinkedList.Tant que vous codez sur l'interface, le développeur peut modifier la mise en œuvre réelle et le reste du programme reste inchangé.
Ceci est également utile lors des tests unitaires, car vous pouvez simuler des interfaces entières, donc, je n'ai pas à aller à une base de données, mais à une implémentation simulée qui renvoie simplement des données statiques, donc je peux tester ma méthode sans se soucier si la base de données est en panne pour maintenance ou non.
la source
Une interface est vraiment un contrat que les classes d'implémentation doivent suivre, c'est en fait la base de presque tous les modèles de conception que je connais.
Dans votre exemple, l'interface est créée car alors tout ce qui EST UNE Pizza, ce qui signifie implémente l'interface Pizza, est garanti d'avoir implémenté
Après votre code mentionné, vous pourriez avoir quelque chose comme ceci:
De cette façon, vous utilisez le polymorphisme et tout ce qui vous importe, c'est que vos objets répondent à order ().
la source
J'ai fait une recherche du mot "composition" sur cette page et je ne l'ai pas vu une seule fois. Cette réponse s'ajoute beaucoup aux réponses susmentionnées.
L'une des raisons absolument cruciales pour utiliser des interfaces dans un projet orienté objet est qu'elles vous permettent de privilégier la composition plutôt que l'héritage. En implémentant des interfaces, vous pouvez dissocier vos implémentations des différents algorithmes que vous leur appliquez.
Ce superbe tutoriel "Motif Décorateur" de Derek Banas (qui - assez drôle - utilise également la pizza comme exemple) en est une illustration intéressante:
https://www.youtube.com/watch?v=j40kRwSm4VE
la source
Les interfaces servent à appliquer la connexion entre différentes classes. par exemple, vous avez une classe pour la voiture et un arbre;
vous souhaitez ajouter une fonctionnalité gravable pour les deux classes. Mais chaque classe a sa propre façon de brûler. donc vous faites simplement;
la source
Vous obtiendrez des interfaces, quand vous en aurez besoin :) Vous pouvez étudier des exemples, mais vous avez besoin du Aha! effet pour vraiment les obtenir.
Maintenant que vous savez ce que sont les interfaces, codez sans elles. Tôt ou tard, vous rencontrerez un problème, où l'utilisation des interfaces sera la chose la plus naturelle à faire.
la source
Je suis surpris que peu de messages contiennent la raison la plus importante pour une interface: Design Patterns . C'est la vue d'ensemble de l'utilisation des contrats, et bien qu'il s'agisse d'une décoration syntaxique pour le code machine (pour être honnête, le compilateur les ignore probablement), l'abstraction et les interfaces sont essentielles pour la POO, la compréhension humaine et les architectures de systèmes complexes.
Développons l'analogie avec la pizza pour dire un repas complet à 3 plats. Nous aurons toujours l'
Prepare()
interface de base pour toutes nos catégories d'aliments, mais nous aurions également des déclarations abstraites pour les sélections de cours (entrée, plat, dessert), et des propriétés différentes pour les types d'aliments (salé / sucré, végétarien / non végétarien, sans gluten, etc.).Sur la base de ces spécifications, nous avons pu implémenter le modèle Abstract Factory pour conceptualiser l'ensemble du processus, mais utiliser des interfaces pour nous assurer que seules les fondations étaient en béton. Tout le reste pourrait devenir flexible ou encourager le polymorphisme, tout en conservant l'encapsulation entre les différentes classes
Course
qui implémentent l'ICourse
interface.Si j'avais plus de temps, j'aimerais en tirer un exemple complet, ou quelqu'un peut l'étendre pour moi, mais en résumé, une interface C # serait le meilleur outil pour concevoir ce type de système.
la source
Voici une interface pour les objets qui ont une forme rectangulaire:
Tout ce qu'il faut, c'est que vous mettiez en œuvre des moyens d'accéder à la largeur et à la hauteur de l'objet.
Définissons maintenant une méthode qui fonctionnera sur n'importe quel objet qui est
IRectangular
:Cela retournera la zone de tout objet rectangulaire.
Implémentons une classe
SwimmingPool
rectangulaire:Et une autre classe
House
qui est également rectangulaire:Cela dit, vous pouvez appeler la
Area
méthode sur les maisons ou les piscines:De cette façon, vos classes peuvent "hériter" du comportement (méthodes statiques) de n'importe quel nombre d'interfaces.
la source
Une interface définit un contrat entre le fournisseur d'une certaine fonctionnalité et les consommateurs correspondants. Il dissocie l'implémentation du contrat (interface). Vous devriez jeter un œil à l'architecture et au design orientés objet. Vous voudrez peut-être commencer par wikipedia: http://en.wikipedia.org/wiki/Interface_(computing)
la source
Quelle ?
Les interfaces sont fondamentalement un contrat que toutes les classes implémentant l'interface doivent suivre. Ils ressemblent à une classe mais n'ont aucune implémentation.
Dans les
C#
noms d'interface par convention est défini par le préfixe d'un «I» donc si vous voulez avoir une interface appelée formes, vous devez le déclarer commeIShapes
Maintenant pourquoi ?
Improves code re-usability
Disons que vous voulez dessiner
Circle
,Triangle.
vous pouvez les regrouper et les appelerShapes
et avoir des méthodes pour dessinerCircle
etTriangle
mais avoir une implémentation concrète serait une mauvaise idée car demain vous pourriez décider d'en avoir 2 de plusShapes
Rectangle
&Square
. Maintenant, lorsque vous les ajoutez, il y a de grandes chances que vous cassiez d'autres parties de votre code.Avec Interface, vous isolez les différentes implémentations du contrat
Scénario en direct, jour 1
On vous a demandé de créer une application pour dessiner des cercles et des triangles
Scénario en direct, jour 2
Si on vous a demandé d'ajouter
Square
et d'y ajouterRectangle
, tout ce que vous avez à faire est de créer l'implémentation pour cela dansclass Square: IShapes
et enMain
ajouter à la listeshapes.Add(new Square());
la source
Il y a beaucoup de bonnes réponses ici, mais je voudrais essayer d'un point de vue légèrement différent.
Vous connaissez peut-être les principes SOLIDES de la conception orientée objet. En résumé:
S - Principe de responsabilité unique O - Principe ouvert / fermé L - Principe de substitution Liskov I - Principe de ségrégation d'interface D - Principe d'inversion de dépendance
Le respect des principes SOLID permet de produire un code propre, bien factorisé, cohérent et faiblement couplé. Étant donné que:
alors tout ce qui aide à la gestion des dépendances est une grande victoire. Les interfaces et le principe d'inversion des dépendances aident vraiment à dissocier le code des dépendances sur des classes concrètes, de sorte que le code peut être écrit et raisonné en termes de comportements plutôt que d'implémentations. Cela permet de diviser le code en composants qui peuvent être composés au moment de l'exécution plutôt qu'au moment de la compilation et signifie également que ces composants peuvent être facilement branchés et déconnectés sans avoir à modifier le reste du code.
Les interfaces aident en particulier avec le principe d'inversion de dépendance, où le code peut être intégré dans une collection de services, chaque service étant décrit par une interface. Les services peuvent ensuite être «injectés» dans les classes lors de l'exécution en les transmettant en tant que paramètre constructeur. Cette technique devient vraiment critique si vous commencez à écrire des tests unitaires et à utiliser le développement piloté par les tests. Essayez! Vous comprendrez rapidement comment les interfaces aident à séparer le code en morceaux gérables qui peuvent être testés individuellement de manière isolée.
la source
Le principal objectif des interfaces est de conclure un contrat entre vous et toute autre classe qui implémente cette interface, ce qui découplera votre code et permettra son extensibilité.
la source
Thérèse nous demande de très bons exemples.
Un autre, dans le cas d'une instruction switch, vous n'avez plus besoin de maintenir et de changer chaque fois que vous voulez que rio exécute une tâche d'une manière spécifique.
Dans votre exemple de pizza, si vous voulez faire une pizza, l'interface est tout ce dont vous avez besoin, à partir de là, chaque pizza prend soin de sa propre logique.
Cela aide à réduire le couplage et la complexité cyclomatique. Vous devez toujours mettre en œuvre la logique, mais il y aura moins de choses à garder à l'esprit dans le cadre plus large.
Pour chaque pizza, vous pouvez ensuite garder une trace des informations spécifiques à cette pizza. Ce que les autres pizzas ont n'a pas d'importance car seules les autres pizzas ont besoin de savoir.
la source
La façon la plus simple de penser aux interfaces est de reconnaître ce que signifie l'héritage. Si la classe CC hérite de la classe C, cela signifie à la fois que:
Ces deux fonctions de l'héritage sont en quelque sorte indépendantes; bien que l'héritage s'applique simultanément aux deux, il est également possible d'appliquer la seconde sans la première. Ceci est utile car autoriser un objet à hériter des membres de deux ou plusieurs classes non liées est beaucoup plus compliqué que d'autoriser un type de chose à être substituable à plusieurs types.
Une interface est un peu comme une classe de base abstraite, mais avec une différence essentielle: un objet qui hérite d'une classe de base ne peut hériter d'aucune autre classe. En revanche, un objet peut implémenter une interface sans affecter sa capacité à hériter de la classe souhaitée ou à implémenter d'autres interfaces.
Une caractéristique intéressante de cela (sous-utilisé dans le cadre .net, à mon humble avis) est qu'ils permettent d'indiquer de manière déclarative les choses qu'un objet peut faire. Certains objets, par exemple, voudront un objet de source de données à partir duquel ils pourront récupérer des choses par index (comme cela est possible avec une liste), mais ils n'auront pas besoin d'y stocker quoi que ce soit. D'autres routines auront besoin d'un objet de dépôt de données où elles peuvent stocker des choses non par index (comme avec Collection.Add), mais elles n'auront pas besoin de lire quoi que ce soit. Certains types de données autorisent l'accès par index, mais ne permettent pas l'écriture; d'autres autoriseront l'écriture, mais ne permettront pas l'accès par index. Certains, bien sûr, permettront les deux.
Si ReadableByIndex et Appendable étaient des classes de base non apparentées, il serait impossible de définir un type qui pourrait être transmis à la fois aux choses qui attendent un ReadableByIndex et aux choses qui attendent un Appendable. On pourrait essayer d'atténuer cela en faisant dériver ReadableByIndex ou Appendable de l'autre; la classe dérivée devrait mettre à disposition des membres publics aux deux fins, mais avertir que certains membres publics pourraient ne pas fonctionner réellement. Certaines des classes et interfaces de Microsoft le font, mais c'est plutôt compliqué. Une approche plus propre consiste à avoir des interfaces pour les différents objectifs, puis à faire en sorte que les objets implémentent des interfaces pour les choses qu'ils peuvent réellement faire. Si l'un avait une interface IReadableByIndex et une autre interface IAppendable,
la source
Les interfaces peuvent également être chaînées pour créer une autre interface. Cette capacité à implémenter plusieurs interfaces donne au développeur l'avantage d'ajouter des fonctionnalités à leurs classes sans avoir à modifier les fonctionnalités de la classe actuelle (principes SOLID)
O = "Les classes doivent être ouvertes pour l'extension mais fermées pour la modification"
la source
Pour moi, un avantage / avantage d'une interface est qu'elle est plus flexible qu'une classe abstraite. Comme vous ne pouvez hériter que d'une classe abstraite mais que vous pouvez implémenter plusieurs interfaces, les modifications d'un système qui hérite d'une classe abstraite à de nombreux endroits deviennent problématiques. S'il est hérité à 100 endroits, un changement nécessite des changements à tous les 100. Mais, avec l'interface, vous pouvez placer le nouveau changement dans une nouvelle interface et utiliser simplement cette interface là où elle est nécessaire (Interface Séq. De SOLID). De plus, l'utilisation de la mémoire semble être moindre avec l'interface car un objet dans l'exemple d'interface n'est utilisé qu'une seule fois en mémoire malgré le nombre d'endroits qui implémentent l'interface.
la source
Les interfaces sont utilisées pour favoriser la cohérence, d'une manière qui est vaguement couplée, ce qui la rend différente de la classe abstraite qui est étroitement couplée. C'est pourquoi elle est également communément définie comme un contrat. défini par l'interface et il n'y a pas d'éléments concrets en son sein.
Je vais juste donner un exemple soutenu par le graphique ci-dessous.
Imaginez dans une usine, il existe 3 types de machines: une machine rectangle, une machine triangle et une machine polygone.Les temps sont compétitifs et vous souhaitez rationaliser la formation des opérateurs.Vous voulez simplement les former à une méthodologie de démarrage et d'arrêt des machines afin que vous ont un bouton de démarrage vert et un bouton d'arrêt rouge. Maintenant, sur 3 machines différentes, vous avez un moyen cohérent de démarrer et d'arrêter 3 types de machines différents.Imaginez maintenant que ces machines sont des classes et que les classes doivent avoir des méthodes de démarrage et d'arrêt, comment vous va conduire à la cohérence entre ces classes qui peuvent être très différentes? L'interface est la réponse.
Un exemple simple pour vous aider à visualiser, on pourrait se demander pourquoi ne pas utiliser la classe abstraite? Avec une interface, les objets n'ont pas besoin d'être directement liés ou hérités et vous pouvez toujours générer de la cohérence entre différentes classes.
la source
Permettez-moi de décrire cela sous un angle différent. Créons une histoire selon l'exemple que j'ai montré ci-dessus;
Program, Machine et IMachine sont les acteurs de notre histoire. Le programme veut s'exécuter mais il n'a pas cette capacité et Machine sait comment l'exécuter. Machine et IMachine sont les meilleurs amis mais le programme n'est pas en accord avec Machine. Ainsi, Program et IMachine concluent un accord et décident qu'IMachine dira à Program comment fonctionner en regardant Machine (comme un réflecteur).
Et le programme apprend à fonctionner à l'aide d'IMachine.
L'interface permet la communication et le développement de projets faiblement couplés.
PS: J'ai la méthode de la classe concrète comme privée. Mon objectif ici est de parvenir à un couplage lâche en empêchant d'accéder aux propriétés et méthodes de classe concrètes, et de ne laisser que le moyen de les atteindre via des interfaces. (J'ai donc défini les méthodes des interfaces de manière explicite).
la source
Je sais que je suis très en retard .. (presque neuf ans), mais si quelqu'un veut une petite explication, vous pouvez opter pour ceci:
En termes simples, vous utilisez Interface lorsque vous savez ce qu'un objet peut faire ou quelle fonction nous allons implémenter sur un objet. Exemple Insérer, mettre à jour et supprimer.
Remarque importante: les interfaces sont TOUJOURS publiques.
J'espère que cela t'aides.
la source