web-dev-qa-db-fra.com

Quand faut-il utiliser des interfaces?

Je sais qu'une interface n'a pas un corps mais une définition de méthode. Mais quand dois-je utiliser les interfaces? Si je fournis à quelqu'un un ensemble d'interfaces sans corps, pourquoi ressentiraient-ils le besoin d'écrire le corps de la fonction? Auraient-ils intérêt à écrire leur propre classe abstraite avec des méthodes abstraites?.

Édité:

Je suppose que l’utilisation des interfaces est plus fréquente lorsque vous faites partie d’une équipe. Supposons que l’équipe A écrive un code pour quelque chose et veuille voir s’il s’agit d’un appel à une méthode. avec le nom getRecords (), est fait ou pas. Cela aidera l’équipe B à écrire le corps de l’interface qui lui a été fournie et l’équipe B doit conserver le même nom de méthode afin que le code de l’équipe A soit exécuté.

Juste une pensée. J'ai peut être tort. Je pense que les interfaces n'ont aucune utilité pour un seul développeur.

Édité:

Merci à tous pour les réponses. Avec ce que vous avez tous répondu, je pense que les interfaces sont plus utiles lorsque vous créez quelque chose comme une API?

40
RKh

Dans des langages tels que Java et les interfaces C # fournissent un moyen pour une classe d'avoir de manière polymorphic . Autrement dit, une classe peut satisfaire plus d’un contrat - elle peut se comporter comme plusieurs types différents, une classe d’un type pouvant être substituée à un autre. Dans d'autres langues, ceci peut aussi être fourni par multiple heritage , mais cette approche présente divers inconvénients. Cependant, avoir une classe qui se comporte comme plusieurs types n'est pas la motivation la plus courante pour utiliser des interfaces.

En programmant des interfaces au lieu de classes, vous pouvez également dissocier votre programme d’implémentations spécifiques. Cela facilite beaucoup le remplacement d'une implémentation de classe par une autre. Ceci est particulièrement utile lorsque vous écrivez des tests unitaires dans lesquels vous souhaiterez peut-être échanger une implémentation de classe heavyweight avec un objet léger mock . Si votre programme n'attend qu'un type d'interface et que l'objet lourd et l'objet fictif implémentent l'interface, ils sont très faciles à remplacer.

En outre, considérons un exemple simple en Java où je dis qu’un programme affiche des pages de données à l’écran. Au départ, je souhaite obtenir les données d'une base de données ou de fichiers XML. Si j'écris mon programme pour qu'il utilise des interfaces, je peux définir une interface comme ceci:

public interface PageDatasource {
    public List<Page> getPages();
}

Et utilisez-le comme suit:

PageDatasource datasource = // insert concrete PageDatasource implementation here
List<Pages> pages = datasource.getPages();
display(pages);

Je peux ensuite écrire des implémentations distinctes de bases de données et XML qui adhèrent à cette interface:

public class DatabasePageDatasource implements PageDatasource {
    public List<Page> getPages() {
        // Database specific code
    }
}

public class XmlPageDatasource implements PageDatasource {
    public List<Page> getPages() {
        // XML specific code
    }
}

Comme j'ai utilisé une interface, je peux maintenant utiliser indifféremment l'implémentation - Base de données ou XML - sans avoir à changer les parties de mon programme qui demandent les données de la page. Les implémentations XML et de base de données font probablement des choses complètement différentes, mais tout ce qui me préoccupe est que l'objet fournissant les données de page implémente l'interface PageDatasource.

49
teabot

Interface Analogie Real Life:

Supposons que vous souhaitiez publier un livre de la bibliothèque . Ce que vous allez faire:
1) Aller à la bibliothèque
2) Trouvez le livre qui vous intéresse
3) Sélectionnez le livre
4) Allez au bureau du bibliothécaire pour leur demander de publier le livre.

Maintenant, ici bibliothécaire est une interface, ce qui signifie que vous n'êtes pas intéressé par qui diable est bibliothécaire, vous êtes seulement intéressé par cette personne assise sur son pupitre (c'est la personne qui a accepté un contrat pour agir en tant que bibliothécaire, ce qui signifie que cette personne a accepté mettre en œuvre tous les comportements de bibliothécaire)

Alors, disons : Le comportement du bibliothécaire est:
1) Issue book
2) Livre de réédition
3) Livre de retour.
la personne désignée comme bibliothécaire (ce qui signifie que l’on accepte d’adapter les trois comportements ci-dessus) doit mettre en œuvre les comportements à sa manière.

Disons que PersonA veut jouer un rôle de bibliothécaire, alors il doit adapter les 3 comportements ci-dessus. En tant que client, nous ne nous soucions pas de la façon dont il exécute ses comportements de bibliothécaire, nous ne nous intéressons qu'à cette personne. Un bibliothécaire est responsable de l'exécution des tâches de bibliothécaire.
Par conséquent, vous ferez référence par interface [allez au guichet du bibliothécaire (qui vous mènera vers des personnes agissant en tant que bibliothécaire)] et laissez-le dans le futur une personne a quitté le poste de bibliothécaire, puis. N'affecte pas vous si vous avez approché le bibliothèque bibliothécaire , au lieu de personne spécifique agissant en tant que bibliothécaire , donc le code à l'interface au lieu de concret est bénéfique. 

class PersonA implements Librarian {
    public void IssueBook(){...}
    public void ReIssueBook(){...}
    public void ReturnBook(){...}

    //Other person related tasks...
}   
23
bharatj

Même en tant que développeur unique, les interfaces peuvent être un outil très utile. Laissez-moi essayer d'illustrer avec un exemple.

Supposons que vous développiez un système permettant de gérer un catalogue de bibliothèque et que celle-ci prête des livres et des DVD. Vous décidez de créer les classes Book et Dvd pour modéliser les éléments prêtés, mais vous souhaitez maintenant implémenter le polymorphisme afin de pouvoir traiter des éléments plutôt que des livres ou des DVD. La question est la suivante: Item doit-il être une classe abstraite ou une interface?

Dans ce cas, vous souhaiterez probablement utiliser une classe abstraite car il existe des fonctionnalités communes à Book et Dvd que vous pouvez fournir par une classe parent, par exemple, extraire ou retourner un élément.

Supposons maintenant que vous souhaitiez implémenter un mécanisme de persistance pour votre catalogue de bibliothèque. Vous avez décidé de stocker des données dans une base de données, des données dans des fichiers XML et des données dans des fichiers séparés par des virgules. La question maintenant est donc: comment pouvez-vous vous y prendre de manière polymorphe, ce qui vous permet de gérer une API de persistance générale? 

Dans ce cas, vous devriez probablement définir une interface pouvant être implémentée par chacune de vos classes fournissant la persistance de la base de données, XML et délimitée par des virgules, car chacun de ces mécanismes de persistance offre des fonctionnalités similaires, à savoir le stockage et la récupération de données, mais chacun sera implémenté de manière très différente. Cela vous permettra de changer facilement le mécanisme de persistance que vous utilisez sans devoir apporter beaucoup de modifications au code qui utilise le mécanisme de persistance.

8
Tony

Les interfaces aident à clarifier la distinction entre les différentes unités fonctionnelles. Une unité dépend d’une autre unité pourdoquelque chose, pas pourêtrequelque chose. Tant que cet autre peutfairece qui est stipulé dans l'interface (pensez contrat), alors il peutêtrerien dans les coulisses.

Par exemple, j'ai un processeur d'entrée qui lit les entrées d'un endroit et les écrit dans un autre endroit. Il se fiche de quoi/où, ou de quoi/où. Tout ce qui importe, c’est que l’entrée d’un certain type de lecteur (en utilisant l’interface IReader) est transférée sur un type d’écrivain (en utilisant l’interface IWriter).

Lors de ma première implémentation, l'implémenteur IReader obtient des éléments d'une base de données SQL et l'implémenteur IWriter les publie via un client de service Web. Cependant, nous créerons éventuellement d'autres implémenteurs pour accéder à d'autres référentiels (sites FTP, répertoires de fichiers sur un lecteur de réseau local, etc.).

Pendant tout ce temps, le processeur au milieu s'en moque et ne change pas. Il parle juste à travers ces interfaces standard.

Théoriquement, vous pourriez utiliser une classe de base (de préférence une classe abstract) à la place d'une interface, mais cela commence à verrouiller plus étroitement vos modules, ce qui rend le système plus difficile à maintenir. Perdre le couplage vous facilite vraiment la vie, même si vous ne faites pas partie d'une équipe de programmeurs. Considérez-vous comme un programmeur différent, chaque fois que vous travaillez sur une partie différente de votre système. Chaque fois que vous revisitez une section donnée, vous devez réapprendre ce qu'elle fait pour la maintenir. Si chaque élément de votre système est étroitement associé à un autre élément, vous devez posséder une connaissance intime et constante de l'ensemble du système, et pas seulement de l'élément sur lequel vous travaillez.

Il y a aussi le concept d'une classe unique implémentant plusieurs interfaces, qui s'apparente presque à un héritage multiple. Dans cette situation, une seule classe peut effectuer plusieurs tâches (ce qui peut être discuté n'est pas très sage, mais au moins c'est possible). Si vous avez choisi d'utiliser une classe de base au lieu d'une interface ci-dessus, cela ne serait pas possible.

8
Mike Hanson

La raison pour laquelle les interfaces existent est due aux 2 concepts principaux de la programmation orientée objet, qui sont "identité" et "fonctionnalité"

Les classes ont une fonctionnalité et une identité. Avec l'héritage, les objets instanciés peuvent avoir beaucoup de fonctionnalités et plusieurs identités.

Les interfaces sont une identité sans fonctionnalité. La fonctionnalité sera fournie par la classe instanciée. 

La troisième forme, un "mixin" est une fonctionnalité sans identité. Les langages de programmation comme Ruby fournissent cette troisième forme d'héritage.

La manière dont vous utilisez une interface varie en fonction du contexte de votre langage de programmation et de votre situation. Rappelez-vous toutefois que les interfaces sont utilisées pour définir les identités à appliquer aux objets.

7
Andrew Keith

À première vue, les classes abstraites et les interfaces semblent être une évidence. Pourquoi ne fournir qu'une interface alors que vous pouvez également fournir une implémentation de base? Après enquête, vous constaterez qu'il y a plus que cela.

Cela dit, il existe un certain nombre de raisons d'utiliser des interfaces. Vous pouvez trouver un article de blog décent sur les différences ici .

Cela dit, considérez le fait que vous pouvez créer une interface (un "contrat" ​​indiquant que votre classe prend définitivement en charge certains appels/appels de signatures de méthode), mais que vous ne pouvez fournir qu'une seule classe abstraite. Tenez également compte du fait que vous pouvez créer une classe abstraite qui implémente également une ou plusieurs interfaces et en hériter. Ce n'est pas un cas Edge. Ceci est en fait fait assez souvent dans les API destinées à une extensibilité élevée.

Consultez le blog que je vous ai indiqué et vous devriez avoir une idée précise du moment d'utilisation de chacun et de la raison de leur utilisation. Je recommande également vivement un bon livre tel que "CLR via C #" de Microsoft Press. Vous apprendrez beaucoup!

7
tobint

Une interface est préférable à une classe abstraite car vous pouvez implémenter plusieurs interfaces et hériter d'une seule classe abstraite.

Alors tu peux faire:

class MyRow extends AbstractRow implements ISearchable, ISortable
{

}

Recherchez également dans StackOverflow des questions similaires, telles que Besoin d’interfaces en c #

4
NDM

Pour compléter les réponses précédentes, les interfaces vous aident lors des tests unitaires en vous permettant d'injecter un objet fictif basé sur une interface dans votre code, ce qui vous permet de simuler des scénarios spécifiques et également d'isoler votre test unitaire à un composant spécifique sans faire appel à des composants externes. .

Par exemple, supposons que vous disposiez d'une classe de logique métier qui utilise une classe de logique de données pour extraire des données d'une source de données, puis les traiter. En créant une interface pour la classe de logique de données dont elle hérite, vous pouvez créer une instance fictive/fictive de cette classe basée sur l'interface et l'injecter dans la classe de logique métier que vous testez d'unité. L'instance simulée peut être définie de manière à pouvoir s'attendre à certains appels de méthodes/propriétés, à lever des exceptions à certains points, à renvoyer des sorties valides, etc. Cela signifie que vos tests unitaires peuvent s'exécuter plus rapidement/potentiellement de manière plus fiable car ils ne dépendent pas des données sous-jacentes la source étant disponible/ne doit pas réellement s'y connecter. Et vous isolez le test unitaire avec une unité de code spécifique.

4
AdaTheDev

Supposons que j'ai la classe FoodEstablishment, maintenant je veux faire une opération CRUD simple, alors comment je le fais? Je définis une interface de service, mais pourquoi?

public interface IFoodEstablishmentService
{
    Task<int> AddAsync(FoodEstablishment oFoodEstablishment);
    FoodEstablishment SelectByFoodEstablishmentId(long id);
    Task<int> UpdateAsync(FoodEstablishment oFoodEstablishment);
    Task<int> DeleteAsync(FoodEstablishment oFoodEstablishment);
}

Ensuite, je vais implémenter ce contrat ou cette interface pour ce service particulier

public class FoodEstablishmentService : IFoodEstablishmentService
{
    public async Task<int> AddAsync(FoodEstablishment oFoodEstablishment)
    {
       // Insert Operation
        return result;
    }

    public FoodEstablishment SelectByFoodEstablishmentId(long id)
    {
        // Select Logic
        return oFoodEstablishment;
    }

    public async Task<int> UpdateAsync(FoodEstablishment oFoodEstablishment)
    {
        // Update Logic
        return result;
    }

    public async Task<int> DeleteAsync(FoodEstablishment oFoodEstablishment)
    {
        // Delete Logic
        return result;
    }

}

Donc, dans mon programme principal ou si je souhaite utiliser Service, je ferai

IFoodEstablishmentService oFoodEstablishmentService =  new FoodEstablishmentService();
FoodEstablishment oFoodEstablishment = // Input might be from views;
oFoodEstablishmentService.AddAsync(oFoodEstablishment);

Jusqu'à présent, cela semble être une étape supplémentaire, alors que nous aurions pu le faire directement

FoodEstablishmentService oFoodEstablishmentService =  new FoodEstablishmentService();
FoodEstablishment oFoodEstablishment = // Input might be from views;
oFoodEstablishmentService.AddAsync(oFoodEstablishment);

Mais attendez de savoir si je devrais passer ma logique d’insertion dans la file plutôt que directement au serveur, attendre la fin de l’opération d’insertion, puis renvoyer le résultat, plutôt que de transmettre la file d’attente et que le travailleur de la file d’attente se charge de ces opérations. file d'attente mais oui, certainement bonne pour l'interface, par exemple :-). Alors maintenant, je crée une autre classe FoodEstablishment qui applique le même contrat IFoodEstablishment.

public class FoodEstablishmentQueueService : IFoodEstablishmentService
{
    public async Task<int> AddAsync(FoodEstablishment oFoodEstablishment)
    {
       // Insert Queue Operation
        return result;
    }

    public FoodEstablishment SelectByFoodEstablishmentId(long id)
    {
        // Select Queue Logic
        return oFoodEstablishment;
    }

    public async Task<int> UpdateAsync(FoodEstablishment oFoodEstablishment)
    {
        // Update Queue Logic
        return result;
    }

    public async Task<int> DeleteAsync(FoodEstablishment oFoodEstablishment)
    {
        // Delete Queue Logic
        return result;
    }
}

Alors maintenant, si je veux utiliser la version en file d'attente, je voudrais juste faire

IFoodEstablishmentService oFoodEstablishmentService =  new FoodEstablishmentQueueService();
FoodEstablishment oFoodEstablishment = // Input might be from views;
oFoodEstablishmentService.AddAsync(oFoodEstablishment);

Nous pourrions faire cela avec l'ancienne méthode utilisant class mais cela limite l'instanciation de classe avec une classe particulière qui est assez rigide à l'extension. Désormais, FoodEstablishmentQueueService peut faire d'autres choses aussi, créer une autre méthode jusqu'à ce que le contrat soit valide afin que l'interface soit contact pour la cohérence. Si vous imitez un type qui exécute la version normale et d'autres qui exécutent la version de la file d'attente ou une version du cache, il peut y avoir des problèmes de signature à moins que le contrat de travail ne le spécifie à l'avance et que les gens ne finissent pas par tout vérifier.

De même, considérons un autre exemple simple d'utilisation de types prédéfinis tels que IEnumerable. Supposons que je passe une liste de la collection FoodEstablishment et que je retourne une liste triée personnalisée

public FoodEstablishment[] SortFoodEstablishment(FoodEstablishment[] list)
{
    foreach(var oFoodEstablishment in list)
    {
    // some logic
    }
    return sortedList;
}

Nous allons donc l'utiliser de cette façon

FoodEstablishment[] list = new FoodEstablishment[]{ //some values }
var listSorted = oFoodEstablishmentService.SortFoodEstablishment(list);

Mais que se passe-t-il si nous envoyons une liste au lieu d'un tableau

List<FoodEstablishment> list = //some values;
var listSorted = oFoodEstablishmentService.SortFoodEstablishment(list);

Nous aurons une erreur, car son implémentation stricte utilisant class est donc nous utilisons IEnumerable <> qui est implémenté par List et qui supprime fondamentalement la dépendance de List à Interface

public IEnumerable<FoodEstablishment> SortFoodEstablishment(IEnumerable<FoodEstablishment> list)
{
    foreach(var oFoodEstablishment in list)
    {
    // some logic
    }
    return sortedList;
}

Donc, généralement, la mise en œuvre dépend énormément de la situation

2
brykneval

Les interfaces et les classes abstraites ont des objectifs différents .. Pour commencer, une classe abstraite n’est utile que pour hériter de - elle ne peut pas être instanciée directement. Vous pouvez placer le code d'implémentation dans une classe abstraite, mais ce code ne peut être appelé qu'à partir de la classe qui l'étend ou par l'intermédiaire de la classe qui l'étend.

exemple: 

public abstract class A {
    public void myFunc() {
        //do some work
    }
}

public class B : A {
    private void someOtherFunc() {
        base.myFunc();
    }
}

public class Z {
    public void exampleFunc() {
        //call public base class function exposed through extending class:
        B myB = new B();
        myB.myFunc();

        //explicitly cast B to A:
        ((A) myB).myFunc();

        //'A' cannot be created directly, do implicit cast down to base class:
        A myA = new B();
        myA.myFunc();
    }
}

Le but d’une interface est de fournir un contrat - c’est-à-dire la classe qui l’implémente a de fournir une implémentation pour les propriétés ou fonctions déclarées dans l’interface, et d’utiliser les mêmes signatures . Cela fournit une sécurité lorsque j'écris du code qui appelle une classe qui implémente une interface, et peu importe de quoi les classes sont dérivées, ou même dans quelle langue elles sont écrites, ou d'où je les ai obtenues. Une autre caractéristique intéressante est que je peux avoir plusieurs implémentations différentes de la même signature de fonction pour différentes interfaces. Vérifiez cet exemple, qui utilise certaines des mêmes classes de l'exemple précédent:

public class Z {
    public void exampleFunc() {
        C myC = new C();
        //these two calls both call the same function:
        myC.myFunc();
        ((IExampleInterface1)myC).myFunc();

        D myD = new D();
        //hmmm... try and work out what happens here, which myFunc() gets called?
        //(clue: check the base class)
        myD.myFunc();

        //call different myFunc() in same class with identical signatures:
        ((IExampleInterface1)myD).myFunc();
        ((IExampleInterface2)myD).myFunc();
    }
}

interface IExampleInterface1
{
    void myFunc();
}

interface IExampleInterface2
{
    void myFunc();
}

public class C : IExampleInterface1
{
    public void myFunc()
    {
        //do stuff
    }
}

public class D : A, IExampleInterface1, IExampleInterface2
{
    void IExampleInterface1.myFunc()
    {
        //this is called when D is cast as IExampleInterface1
    }

    void IExampleInterface2.myFunc()
    {
        //this is called when D is cast as IExampleInterface2
    }
}
2
slugster

C'est un problème de design (je parle du monde Java).

L'interface vous permet de définir le comportement et la structure du composant (classe) de votre logiciel sans vous contraindre à l'exécution.

La classe abstraite, à la place, vous donne un comportement par défaut pour une méthode: utile si vous êtes sûr que ce code ne changera peut-être pas dans le temps de lecture de l'application.

Exemple:

Vous disposez d'une application Web du système commercial qui envoie un courrier électronique dans certaines situations (inscription d'un nouvel utilisateur).

Vous pouvez utiliser la classe abstraite SenderCommunication avec la méthode boolean sendWithSuccefull (...) si vous êtes certain que le comportement ne changera pas.

Vous pouvez utiliser l'interface InterfaceSenderCommunication avec la méthode boolean sendWithSuccefull (...) si vous n'êtes pas sûr que le comportement ne changera pas souvent.

Bien entendu, le jugement de "changement souvent - pas souvent" dépend de 2 éléments:

  • Combien de temps dois-je consacrer à la synchronisation De l’ancien code avec les nouvelles spécifications?
  • Combien le client paie? ;)
2
alepuzio

Java lang ne prend pas en charge l'héritage multiple car des interfaces sont utilisées pour atteindre l'objectif.

Pour qu'une classe soit abstraite, une seule méthode doit être abstraite; Alors que dans le cas de l'interface , Toutes les méthodes sont abstraites.

1
Ashish

L'interface de classe abstraite v/s est toujours un sujet de discussion entre développeurs. J'ajouterais 5 cents . Utilisez la classe abstraite lorsque vous souhaitez étendre une base de données commune et que vous souhaitez fournir une implémentation par défaut à la méthode abstraite.

Utilisez interface lorsque vous souhaitez implémenter exactement toutes les méthodes abstraites dans la classe implémentant l'interface et qu'aucun corps par défaut pour une méthode ne peut être fourni.

1

Une des raisons d'utiliser des interfaces est lorsqu'une classe implémente un certain nombre d'interfaces. Une classe abstraite ne peut pas faire ça.

Un exemple est une classe qui gère les mouvements de la souris et les touches utilisées implémenteront les interfaces (fictives) IMouseMove et IKeyPress.

1
paul

De plus, l'utilisation d'interfaces facilite les tests unitaires. Pour tester des classes qui dépendent d'interfaces, et tant que vous utilisez une sorte d'injection de dépendance, vous pouvez créer des classes de stub qui implémentent les interfaces dépendantes, ou vous pouvez utiliser un moteur de moquage.

1
Konamiman

Interface conserve la définition et l'implémentation Separate.So, au cas où vous souhaiteriez une implémentation spécifique au fournisseur, c'est la meilleure façon de le faire.

JDBC en est un exemple concret. Si vous voyez l'API Statemnet, PreparedStatemnet Tout est une interface, mais l'implémentation est spécifique au fournisseur, comme Oracle ojdbc jar.

Si vous souhaitez modifier la base de données, il vous suffit de sélectionner le pilote dans le nom de la classe de connexion. C'est l'avantage d'utiliser l'interface

1
Brijesh k

Utilisations de l'interface: _

  1. Pour définir un contrat
  2. Pour relier des classes non liées à a capacités (par exemple, les classes implémentant une interface sérialisable peuvent n’avoir aucune relation entre elles sauf la mise en œuvre de cette interface.
  3. Pour fournir une implémentation interchangeable, par ex. Strategy_pattern

Si vous recherchez Java comme langage de programmation, interface dispose de peu de fonctionnalités supplémentaires avec l’ajout des méthodes default et static depuis la version Java-8.

Référez-vous à cette question pour plus de détails:

Pourquoi utiliser les interfaces, l'héritage multiple par rapport aux interfaces, les avantages des interfaces?

0
Ravindra babu