Pour moi, lorsque j'ai commencé, l'intérêt de ces éléments n'est devenu clair que lorsque vous avez cessé de les considérer comme des éléments destinés à rendre votre code plus facile/plus rapide à écrire - ce n'est pas leur but. Ils ont un certain nombre d'utilisations :
(L'analogie avec la pizza va être perdue, car il n'est pas très facile de visualiser l'utilisation de ce système).
Disons que vous créez un jeu simple à l'écran et qu'il comporte des créatures avec lesquelles vous interagissez.
R : Ils peuvent rendre votre code plus facile à maintenir à l'avenir en introduisant un couplage lâche entre votre front-end et votre implémentation back-end.
Vous pourriez écrire cela pour commencer, car il n'y aura que des trolls :
// This is our back-end implementation of a troll
class Troll
{
void Walk(int distance)
{
//Implementation here
}
}
Partie avant :
function SpawnCreature()
{
Troll aTroll = new Troll();
aTroll.Walk(1);
}
Deux semaines plus tard, les responsables du marketing décident que vous avez également besoin d'orques, car ils en ont entendu parler sur Twitter, et vous devez donc faire quelque chose comme ça :
class Orc
{
void Walk(int distance)
{
//Implementation (orcs are faster than trolls)
}
}
Partie avant :
void SpawnCreature(creatureType)
{
switch(creatureType)
{
case Orc:
Orc anOrc = new Orc();
anORc.Walk();
case Troll:
Troll aTroll = new Troll();
aTroll.Walk();
}
}
Et vous pouvez voir comment cela commence à devenir désordonné. Vous pourriez utiliser une interface ici pour que votre front-end soit écrit une fois et (voici le point important) testé, et que vous puissiez ensuite ajouter d'autres éléments back-end si nécessaire :
interface ICreature
{
void Walk(int distance)
}
public class Troll : ICreature
public class Orc : ICreature
//etc
La partie avant est alors :
void SpawnCreature(creatureType)
{
ICreature creature;
switch(creatureType)
{
case Orc:
creature = new Orc();
case Troll:
creature = new Troll();
}
creature.Walk();
}
Le frontal ne s'intéresse maintenant qu'à l'interface ICreature - il ne se soucie pas de l'implémentation interne d'un troll ou d'un orc, mais seulement du fait qu'ils implémentent ICreature.
Un point important à noter lorsque l'on examine cette question 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 pourriez extraire la création vers une usine :
public class CreatureFactory {
public ICreature GetCreature(creatureType)
{
ICreature creature;
switch(creatureType)
{
case Orc:
creature = new Orc();
case Troll:
creature = new Troll();
}
return creature;
}
}
Et notre front-end deviendrait alors :
CreatureFactory _factory;
void SpawnCreature(creatureType)
{
ICreature creature = _factory.GetCreature(creatureType);
creature.Walk();
}
Le front-end n'a même pas besoin d'avoir une référence à la bibliothèque où Troll et Orc sont implémentés (à condition que la fabrique soit dans une bibliothèque séparée) - il n'a pas besoin de savoir quoi que ce soit à leur sujet.
B : Disons que vous avez une fonctionnalité que seules certaines créatures auront dans votre structure de données autrement homogène. par exemple
interface ICanTurnToStone
{
void TurnToStone();
}
public class Troll: ICreature, ICanTurnToStone
La partie avant pourrait alors être :
void SpawnCreatureInSunlight(creatureType)
{
ICreature creature = _factory.GetCreature(creatureType);
creature.Walk();
if (creature is ICanTurnToStone)
{
(ICanTurnToStone)creature.TurnToStone();
}
}
C : Utilisation de l'injection de dépendances
La plupart des cadres d'injection de dépendances fonctionnent lorsqu'il existe un couplage très lâche entre le code frontal et l'implémentation dorsale. Si nous prenons notre exemple de fabrique ci-dessus et que notre fabrique implémente une interface :
public interface ICreatureFactory {
ICreature GetCreature(string creatureType);
}
Notre frontal pourrait alors avoir cette injection (par exemple un contrôleur API MVC) par le biais du constructeur (typiquement) :
public class CreatureController : Controller {
private readonly ICreatureFactory _factory;
public CreatureController(ICreatureFactory factory) {
_factory = factory;
}
public HttpResponseMessage TurnToStone(string creatureType) {
ICreature creature = _factory.GetCreature(creatureType);
creature.TurnToStone();
return Request.CreateResponse(HttpStatusCode.OK);
}
}
Avec notre cadre DI (par exemple Ninject ou Autofac), nous pouvons les configurer de sorte qu'au moment de l'exécution, une instance de CreatureFactory soit 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 fantaisie (par exemple, si l'implémentation concrète nécessite un accès à la base de données, nous ne voulons pas que nos tests unitaires en dépendent) et tester facilement le code dans notre contrôleur.
D : Il y a d'autres utilisations, par exemple, vous avez deux projets A et B qui, pour des raisons "historiques", ne sont pas bien structurés, et A a une référence à B.
Vous trouvez alors une fonctionnalité dans B qui doit 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 en A implémente ensuite. Votre méthode en B peut recevoir 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.
0 votes
J'ai l'impression qu'il y a des doublons de cette question ici sur SO, mais ils semblent tous expliquer la partie contrat d'une interface donc je ne suis pas sûr qu'ils s'appliquent.
10 votes
En essayant d'être un utilisateur sympa et ordonné, j'ai tendance à chercher d'abord ma réponse dans divers forums avant de poster quelque chose. Malheureusement, la plupart d'entre eux ont commencé plus tard et le reste ne m'a pas aidé. J'avais déjà du mal à répondre à la question de base "Pourquoi le faire ?", car il me semblait qu'il était inutile de trop compliquer les choses. Merci à tous pour les réponses très rapides. Je dois d'abord les digérer toutes, mais je pense que j'ai maintenant une assez bonne idée de leur intérêt. Il semble que j'ai toujours envisagé la question sous un angle différent. Merci beaucoup pour votre aide.
0 votes
Je veux juste dire que les interfaces sont difficiles à comprendre. Vous devez faire un grand effort pour les maîtriser. Si vous avez le temps, vous devriez lire ce livre : Injection de dépendances dans .NET . Il vous fera également découvrir de nombreuses idées connexes. Notez également que Python et C# sont assez différents. C# est typée statiquement.
1 votes
stackoverflow.com/questions/3971326/le-point-de-une-interface/
1 votes
Les interfaces permettent également d'établir l'héritage, comme pour
struct
types.0 votes
stackoverflow.com/a/24436493/1286942
5 votes
Hmm, l'OP demandait "D'après ce que j'ai compris, l'interface est une sorte de contournement de la multi-héritage inexistante qui n'existe pas en C#. (En dehors de cela, dans l'exemple cité de la pizza du manuel), j'utiliserais simplement une classe de base au lieu d'une interface". La plupart des réponses ont donné un exemple qui peut être implémenté par une classe de base (abstraite), ou ont donné un exemple pour montrer comment l'interface est nécessaire pour le scénario de multi-héritage. Ces réponses sont toutes bonnes, mais ne font-elles pas que répéter ce que le PO sait déjà ? Pas étonnant que le PO ait fini par choisir une réponse sans exemple. LOL