2009-11-23 4 views
3

Désolé de poser une question générique sich, mais j'ai étudié ceux-ci et, en dehors de la programmation de la tête transportant ce membre DOIT être dans une classe, je ne vois tout simplement pas d'avantages.Quelle est la valeur d'Interfaces?

+13

"Autre que le point entier du mécanisme, je ne vois aucun point du mécanisme." –

+0

LOL à la citation - d'où vient-elle? – Mathias

+0

Juste sur le dessus de ma tête, en fait. J'ai trouvé la question humble, alors j'ai fait un commentaire en tant que tel. C'est une question légitime, donc je n'ai pas vraiment envie de déchirer le PO. –

Répondre

7

Il y a deux (de base) parties à la programmation orientée objet qui donnent du mal pour les nouveaux arrivants; le premier est inheritance et le second est composition. Ce sont les plus difficiles à «obtenir»; et une fois que vous comprenez ces tout le reste est tellement plus facile.

Ce à quoi vous faites référence est composition - par exemple, que fait une classe? Si vous allez la route d'héritage, elle dérive d'une classe abstraite (par exemple Dog IS A Animal). Si vous utilisez la composition, alors vous êtes en train d'établir un contrat (une voiture a un conducteur/prêt/assurance). Toute personne qui implémente votre interface doit implémenter les méthodes de cette interface.

Ceci permet un couplage lâche; et ne vous attache pas dans le modèle d'héritage où il ne rentre pas.

Lorsque l'héritage convient, utilisez-le; mais si la relation entre deux classes est de nature contractuelle, ou HAS-A par rapport à IS-A, utilisez une interface pour modéliser cette partie.

Pourquoi utiliser des interfaces?

Pour un exemple pratique, sautons dans une application métier. Si vous avez un référentiel vous voudrez faire de la couche au-dessus de votre dépôt celles des interfaces. De cette façon, si vous devez changer quoi que ce soit dans la façon dont le référentiel fonctionne, vous n'affecterez rien car ils obéissent tous aux mêmes contrats.

Voici notre dépôt:

public interface IUserRepository 
{ 
    public void Save(); 
    public void Delete(int id); 
    public bool Create(User user); 
    public User GetUserById(int id); 
} 

Maintenant, je peux mettre en œuvre ce référentiel dans une classe:

public class UserRepository : IRepository 
{ 
    public void Save() 
    { 
     //Implement 
    } 

    public void Delete(int id) 
    { 
     //Implement 
    } 

    public bool Create(User user) 
    { 
     //Implement 
    } 
    public User GetUserById(int id) 
    { 
     //Implement 
    } 

} 

Ce sépare l'interface de ce qui est l'appelant. Je pourrais changer cette classe de Linq-To-SQL à des procédures SQL ou stockées inline, et tant que j'ai implémenté l'interface IUserRepository, personne ne serait plus sage; et le meilleur de tous, il n'y a pas de classes qui dérivent de ma classe qui pourrait potentiellement être énervé de mon changement.

Héritage et composition: Best Friends

Héritage et composition sont destinés à lutter contre différents problèmes. Utilisez chacun où il convient, et il y a des sous-ensembles entiers de problèmes où vous utilisez les deux.

+0

Je ne veux pas supprimer ma réponse parce qu'elle donne des mots différents pour la même idée, mais j'aime beaucoup votre réponse. Je pense qu'un peu d'explication pourquoi vous voudriez un et pas l'autre me donnerait envie d'enlever ma réponse. –

+0

Ironiquement, j'écris ça en ce moment. –

+0

Polymorphisme, quelqu'un? http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming#C.23 –

1

Il vous permet de séparer l'implémentation de la définition.

Par exemple, je peux définir une interface à laquelle une section de mon code est codée - en ce qui concerne, elle appelle des membres sur l'interface. Ensuite, je peux échanger les implémentations à l'intérieur et à l'extérieur comme je le souhaite - si je veux créer une fausse version du composant d'accès à la base de données, alors je peux.

Les interfaces sont les éléments de base de composants logiciels

0

Parler du point de vue de Java, vous pouvez créer une interface, indiquant toutes les classes qui transposent interface, que « vous devez mettre en œuvre ces méthodes » mais vous ne introduire une autre classe dans la hiérarchie. Ceci est souhaitable car vous voudrez peut-être garantir que certains mécanismes existent quand vous voulez que des objets de bases différentes aient la même sémantique de code (c'est-à-dire les mêmes méthodes qui sont codées comme appropriées dans chaque classe) ne veux pas créer une classe abstraite, ce qui vous limiterait en ce que vous ne pouvez pas hériter d'une autre classe.

juste une pensée ... je seulement bricoler avec Java. Je ne suis pas un expert.

S'il vous plaît voir mes pensées ci-dessous. Deux appareils différents doivent recevoir des messages de notre ordinateur. l'un réside sur Internet et utilise http comme protocole de transport. l'autre se trouve à 10 mètres, se connecter via USB.

Notez que cette syntaxe est un pseudo-code.

 
    interface writeable 
    { 
     void open(); 
     void write(); 
     void close(); 
    } 

    class A : HTTP_CONNECTION implements writeable 
    { 
     //here, opening means opening an HTTP connection. 
     //maybe writing means to assemble our message for a specific protocol on top of 
     //HTTP 
     //maybe closing means to terminate the connection 
    } 

    class B : USB_DEVICE implements writeable 
    { 
     //open means open a serial connection 
     //write means write the same message as above, for a different protocol and device 
     //close means to release USB object gracefully. 
    } 
0

En Java, les interfaces vous permettent de référencer n'importe quelle classe qui implémente l'interface. Ceci est similaire au sous-classement mais il y a des moments où vous voulez vous référer à des classes de hiérarchies complètement différentes comme si elles étaient du même type.

2

Pour qu'une classe donnée puisse hériter de plusieurs sources, tout en ne héritant que d'une classe parente unique.

Certains langages de programmation (C++ est l'exemple classique) permettent à une classe d'hériter de plusieurs classes; Dans ce cas, les interfaces ne sont pas nécessaires (et, en général, n'existent pas.)

Cependant, lorsque vous vous retrouvez dans une langue comme Java ou C# où l'héritage multiple n'est pas autorisé, vous avez besoin d'un mécanisme différent permettant à une classe d'hériter de plusieurs sources, c'est-à-dire de représenter plus d'une relation "est-a". Entrez les interfaces. Donc, il vous permet de définir, littéralement, des interfaces - une classe implémentant une interface implémentera un ensemble donné de méthodes, sans avoir à spécifier quoi que ce soit sur la façon dont ces méthodes sont réellement écrites.

+1

Vous pouvez facilement créer une interface en C++, FWIW, et c'est parfois utile. Utilisez simplement des fonctions membres virtuels pures dans une classe, pas de membres de données, et appelez-la une interface. –

+0

Accordée. (Je l'ai fait même maintenant, maintenant que je repense à mes jours C++.) –

0

Les interfaces créent une couche isolante entre un consommateur et un fournisseur. Cette couche d'isolation peut être utilisée pour différentes choses. Mais globalement, s'ils sont utilisés correctement, ils réduisent la densité de dépendance (et la complexité qui en résulte) dans l'application.

0

Je souhaite soutenir la réponse d'Electron comme étant la réponse la plus valide.

La programmation orientée objet facilite la déclaration des contrats. Une déclaration de classe est le contrat. Le contrat est un engagement de la classe à fournir des fonctionnalités en fonction des types/signatures déclarés par la classe. Dans les langues oo communes, chaque classe a un contrat public et protégé.

De toute évidence, nous savons tous qu'une interface est un modèle de classe non rempli vide qui peut être autorisé à se faire passer pour une classe. Mais pourquoi avoir des contrats de classe vides non remplis?

Une classe implémentée a tous ses contrats remplis spontanément.

Une classe abstraite est un contrat partiellement rempli.

Une classe projette spontanément une personnalité grâce à ses caractéristiques mises en œuvre en indiquant qu'elle est qualifiée pour une certaine description de travail. Toutefois, il pourrait également projeter plus d'une personnalité pour se qualifier pour plus d'une description de travail.

Mais pourquoi une classe Motorcar ne devrait-elle pas présenter honnêtement sa personnalité complète plutôt que de se cacher derrière les rideaux de multiples personnalités? C'est parce que, une Classe de Vélo, Bateau ou Skateboard qui souhaite se présenter autant qu'un mode de Transport ne souhaite pas mettre en œuvre toutes les complexités et contraintes d'une Motorcar. Un bateau doit être capable de voyager dans l'eau, ce qui n'est pas le cas pour une voiture. Alors pourquoi ne pas donner à un Motorcar toutes les caractéristiques d'un bateau aussi - bien sûr, la réponse à une telle proposition serait - êtes-vous kiddin?

Parfois, nous souhaitons simplement déclarer un contrat non satisfait sans déranger avec la mise en œuvre. Une classe abstraite totalement insatisfaite est simplement une interface. Peut-être, une interface est semblable aux formes juridiques vierges que vous pourriez acheter à partir d'un magasin stationnaire.

Par conséquent, dans un environnement qui permet des héritages multiples, les interfaces/classes totalement abstraites sont utiles lorsque nous souhaitons simplement déclarer des contrats non remplis que quelqu'un d'autre pourrait remplir.

Dans un environnement qui interdit les héritages multiples, l'utilisation d'interfaces est le seul moyen de permettre à une classe d'implémentation de projeter plusieurs personnalités.

Tenir compte

interface Transportation 
{ 
    takePassengers(); 
    gotoDestination(Destination d); 
} 

class Motorcar implements Transportation 
{ 
    cleanWindshiedl(); 
    getOilChange(); 
    doMillionsOtherThings(); 
    ... 
    takePassengers(); 
    gotoDestination(Destination d); 
} 

class Kayak implements Transportation 
{ 
    paddle(); 
    getCarriedAcrossRapids(); 
    ... 
    takePassengers(); 
    gotoDestination(Destination d); 
} 

Une activité de transport doit être aveugle aux alternatives de transport de millions. Parce qu'il veut juste appeler

Transportation.takePassengers or 
Transportation.gotoDestination 

parce qu'il demande le transport mais il est rempli. C'est une réflexion et une programmation modulaires, car nous ne voulons pas nous limiter à un véhicule motorisé ou un kayak pour le transport. Si nous nous limitons à tout le transport que nous connaissons, nous devrons passer beaucoup de temps à découvrir toutes les technologies de transport actuelles et voir si elles correspondent à notre plan d'activités.

Nous ne savons pas non plus qu'à l'avenir, un nouveau mode de transport appelé AntiGravityCar serait développé. Et après avoir passé tant de temps à accommoder inutilement tous les modes de transport que nous connaissons, nous constatons que notre routine ne nous permet pas d'utiliser AntiGravityCar. Mais avec un contrat spécifique qui aveugle toute technologie autre que celle qu'elle requiert, non seulement nous ne perdons pas de temps à considérer toutes sortes de comportements de transports différents, mais tout futur développement de transport qui implémente l'interface Transport peut simplement s'intégrer dans l'activité sans plus tard.

5

J'allais laisser George faire remarquer que vous pouvez maintenant consommer l'interface plutôt que la classe concrète. Il semble que tout le monde ici comprend ce qu'est une interface et comment la définir, mais la plupart n'ont pas réussi à expliquer le point clé d'une manière facile à comprendre - et quelque chose que la plupart des cours ne parviennent pas à souligner. à des pailles ou le comprendre par vous-même alors je vais essayer de l'épeler d'une manière qui ne nécessite pas non plus. Donc, j'espère que vous ne serez pas en train de penser "et alors, cela semble toujours une perte de temps/effort/code."

public interface ICar 
{ 
    public bool EngineIsRunning{ get; } 
    public void StartEngine(); 
    public void StopEngine(); 
    public int NumberOfWheels{ get; } 
    public void Drive(string direction); 
} 

public class SportsCar : ICar 
{ 
    public SportsCar 
    { 
     Console.WriteLine("New sports car ready for action!"); 
    } 

    public bool EngineIsRunning{ get; protected set; } 

    public void StartEngine() 
    { 
     if(!EngineIsRunning) 
     { 
      EngineIsRunning = true; 
      Console.WriteLine("Engine is started."); 
     } 
     else 
      Console.WriteLine("Engine is already running."); 
    } 

    public void StopEngine() 
    { 
     if(EngineIsRunning) 
     { 
      EngineIsRunning = false; 
      Console.WriteLine("Engine is stopped."); 
     } 
     else 
      Console.WriteLine("Engine is already stopped."); 
    } 

    public int NumberOfWheels 
    { 
     get 
     { 
      return 4; 
     } 
    } 

    public void Drive(string direction) 
    { 
     if (EngineIsRunning) 
      Console.WriteLine("Driving {0}", direction); 
     else 
      Console.WriteLine("You can only drive when the engine is running."); 
    } 
} 

public class CarFactory 
{ 
    public ICar BuildCar(string car) 
    { 
     switch case(car) 
      case "SportsCar" : 
       return Activator.CreateInstance("SportsCar"); 
      default : 
       /* Return some other concrete class that implements ICar */ 
    } 
} 

public class Program 
{ 
    /* Your car type would be defined in your app.config or some other 
    * mechanism that is application agnostic - perhaps by implicit 
    * reference of an existing DLL or something else. My point is that 
    * while I've hard coded the CarType as "SportsCar" in this example, 
    * in a real world application, the CarType would not be known at 
    * design time - only at runtime. */ 
    string CarType = "SportsCar"; 

    /* Now we tell the CarFactory to build us a car of whatever type we 
    * found from our outside configuration */ 
    ICar car = CarFactory.BuildCar(CarType); 

    /* And without knowing what type of car it was, we work to the 
    * interface. The CarFactory could have returned any type of car, 
    * our application doesn't care. We know that any class returned 
    * from the CarFactory has the StartEngine(), StopEngine() and Drive() 
    * methods as well as the NumberOfWheels and EngineIsRunning 
    * properties. */ 
    if (car != null) 
    { 
     car.StartEngine(); 
     Console.WriteLine("Engine is running: {0}", car.EngineIsRunning); 
     if (car.EngineIsRunning) 
     { 
      car.Drive("Forward"); 
      car.StopEngine(); 
     } 
    } 
} 

Comme vous pouvez le voir, nous pourrions définir tout type de voiture, et aussi longtemps que cette voiture implémente le ICar d'interface, il aura les propriétés et méthodes prédéfinies que nous pouvons appeler de notre application principale. Nous Vous n'avez pas besoin de savoir quel type de voiture est - ou même quel type de classe a été retourné par la méthode CarFactory.BuildCar(), il pourrait renvoyer une instance de type "DragRacer" pour tout ce qui nous intéresse, tout ce que nous devons savoir est que DragRacer implémente ICar et que nous pouvons continuer la vie comme d'habitude

Dans une application du monde réel, imaginez plutôt IDataStore où nos classes concrètes de stockage de données donnent accès à un magasin de données sur disque, ou sur le réseau, une base de données, Poussette e, on s'en fout de quoi - tout ce qu'on voudrait, c'est que la classe concrète renvoyée par notre usine de classe implémente l'interface IDataStore et qu'on puisse appeler les méthodes et propriétés sans avoir besoin de connaître l'architecture sous-jacente de la classe. Une autre implication du monde réel (pour .NET au moins) est que si la personne qui a codé la classe de voiture de sport apporte des modifications à la bibliothèque qui contient l'implémentation de la voiture de sport et la recompile, et vous avez fait une référence à leur bibliothèque que vous aurez besoin de recompiler - alors que si vous avez codé votre application contre ICar, vous pouvez simplement remplacer la DLL avec leur nouvelle version et vous pouvez continuer comme d'habitude.

+0

Grande explication. C'est ce que je voulais dire, mais vous l'avez fait beaucoup mieux. +1 –

0

Aucune des réponses mentionne encore le mot clé: substituabilité. Tout objet qui implémente l'interface Foo peut être remplacé par "une chose qui implémente Foo" dans tout code nécessitant ce dernier. Dans de nombreux cadres, un objet doit donner une réponse unique à la question «Quel type de chose êtes-vous» et une seule réponse à «De quel type êtes-vous dérivé»? néanmoins, il peut être utile qu'un type soit substituable à de nombreux types de choses différentes. Les interfaces permettent cela. Un VolkswagonBeetleConvertible est dérivé de VolkswagonBeetle, et un FordMustangConvertible est dérivé de FordMustang. Les deux VolkswagonBeetleConvertible et FordMustangConvertible implémentent IOpenableTop, même si aucun type parent de classe ne le fait. Par conséquent, les deux types dérivés mentionnés peuvent être remplacés par "une chose qui implémente IOpenableTop".

Questions connexes