Pouvons-nous exposer des interfaces dans Ruby comme nous le faisons en java et appliquer les modules ou classes Ruby pour implémenter les méthodes définies par interface.
Une façon est d'utiliser l'héritage et method_missing pour obtenir la même chose, mais existe-t-il une autre approche plus appropriée disponible?
Réponses:
Ruby a des interfaces comme n'importe quel autre langage.
Notez qu'il faut faire attention à ne pas confondre le concept d' Interface , qui est une spécification abstraite des responsabilités, garanties et protocoles d'une unité avec le concept de
interface
qui est un mot-clé dans la programmation Java, C # et VB.NET langues. Dans Ruby, nous utilisons le premier tout le temps, mais le second n'existe tout simplement pas.Il est très important de distinguer les deux. Ce qui est important, c'est l' interface , pas le
interface
. Le neinterface
vous dit quasiment rien d'utile. Rien ne le démontre mieux que les interfaces de marqueurs en Java, qui sont des interfaces qui n'ont aucun membre: il suffit de jeter un œil àjava.io.Serializable
etjava.lang.Cloneable
; ces deuxinterface
signifient des choses très différentes, mais ils ont exactement la même signature.Donc, si deux
interface
s que les choses moyennes différentes, ont la même signature, ce exactement est lainterface
même vous garantir?Un autre bon exemple:
Quelle est l' interface de
java.util.List<E>.add
?element
est dans la collectionEt lequel de ceux-ci apparaît réellement dans le
interface
? Aucun! Il n'y a rien dans leinterface
qui dit que laAdd
méthode doit même ajouter du tout, elle pourrait tout aussi bien supprimer un élément de la collection.C'est une implémentation parfaitement valide de cela
interface
:Un autre exemple: où
java.util.Set<E>
est-il dit qu'il s'agit, vous savez, d'un ensemble ? Nulle part! Ou plus précisément, dans la documentation. En anglais.Dans presque tous les cas
interfaces
, à la fois à partir de Java et .NET, toutes les informations pertinentes se trouvent en fait dans la documentation, pas dans les types. Donc, si les types ne vous disent rien d'intéressant de toute façon, pourquoi les garder du tout? Pourquoi ne pas s'en tenir uniquement à la documentation? Et c'est exactement ce que fait Ruby.Notez qu'il existe d' autres langages dans lesquels l' interface peut en fait être décrite de manière significative. Cependant, ces langages n'appellent généralement pas la construction qui décrit l' interface "
interface
", ils l'appellenttype
. Dans un langage de programmation à typage dépendant, vous pouvez, par exemple, exprimer les propriétés qu'unesort
fonction renvoie une collection de la même longueur que l'original, que chaque élément qui est dans l'original est également dans la collection triée et qu'aucun élément plus grand apparaît devant un élément plus petit.Donc, en bref: Ruby n'a pas d'équivalent à un Java
interface
. Il ne , cependant, ont un équivalent à Java Interface , et il est exactement le même que dans Java: documentation.De plus, tout comme en Java, les tests d'acceptation peuvent également être utilisés pour spécifier des interfaces .
En particulier, dans Ruby, l' interface d'un objet est déterminée par ce qu'il peut faire , et non par ce qu'il
class
est, ou cemodule
dans quoi il se mélange. Tout objet qui a une<<
méthode peut être ajouté. Ceci est très utile dans les tests unitaires, où vous pouvez simplement passer unArray
ou unString
au lieu d'un plus compliquéLogger
, même siArray
etLogger
ne partagez pas un explicite misinterface
à part le fait qu'ils ont tous les deux une méthode appelée<<
.Un autre exemple est
StringIO
, qui implémente la même Interface queIO
et donc une grande partie de l' Interface deFile
, mais sans partager d'ailleurs aucun ancêtre communObject
.la source
interface
est inutile, manquant le point de son utilisation. Il aurait été plus facile de dire que ruby est typé dynamiquement et qu'il a un objectif différent à l'esprit et rend les concepts comme IOC inutiles / indésirables. C'est un changement difficile si vous êtes habitué à la conception par contrat. Quelque chose dont Rails pourrait bénéficier, ce que l'équipe de base a réalisé comme vous pouvez le voir sur les dernières versions.interface
- clé Java peut ne pas fournir toutes les informations pertinentes, mais il fournit un endroit évident pour mettre la documentation. J'ai écrit une classe en Ruby qui implémente (assez de) IO, mais je l'ai fait par essais et erreurs et je n'étais pas trop satisfait du processus. J'ai également écrit plusieurs implémentations de ma propre interface, mais documenter les méthodes requises et ce qu'elles sont censées faire pour que les autres membres de mon équipe puissent créer des implémentations s'est avéré un défi.interface
construction n'est en effet nécessaire que pour traiter différents types de la même manière dans les langages à héritage unique à typage statique (par exemple, traiterLinkedHashSet
et lesArrayList
deux comme aCollection
), elle n'a pratiquement rien à voir avec Interface comme le montre cette réponse. Ruby n'est pas typé statiquement donc il n'y a pas besoin de la construction .Essayez les "exemples partagés" de rspec:
https://www.relishapp.com/rspec/rspec-core/v/3-5/docs/example-groups/shared-examples
Vous écrivez une spécification pour votre interface, puis mettez une ligne dans la spécification de chaque implémenteur, par exemple.
Exemple complet:
Mise à jour : Huit ans plus tard (2020), ruby prend désormais en charge les interfaces de type statique via sorbet. Voir Classes et interfaces abstraites dans la documentation sorbet.
la source
Ruby n'a pas cette fonctionnalité. En principe, il n'en a pas besoin car Ruby utilise ce qu'on appelle le typage canard .
Il y a peu d'approches que vous pouvez adopter.
Ecrire des implémentations qui déclenchent des exceptions; si une sous-classe tente d'utiliser la méthode non implémentée, elle échouera
Avec ci-dessus, vous devez écrire un code de test qui applique vos contrats (quel autre message ici appelle incorrectement Interface )
Si vous vous retrouvez à écrire des méthodes vides comme ci-dessus tout le temps, écrivez un module d'aide qui capture cela
Maintenant, combinez ce qui précède avec les modules Ruby et vous êtes proche de ce que vous voulez ...
Et puis tu peux faire
Permettez-moi de souligner encore une fois: il s'agit d'un élément rudimentaire, car tout dans Ruby se produit au moment de l'exécution; il n'y a pas de vérification au moment de la compilation. Si vous associez cela à des tests, vous devriez être en mesure de détecter les erreurs. Encore plus loin, si vous allez plus loin, vous pourriez probablement être en mesure d'écrire une interface qui vérifie la classe la première fois qu'un objet de cette classe est créé; rendre vos tests aussi simples que d'appeler
MyCollection.new
... ouais, par dessus :)la source
Comme tout le monde l'a dit ici, il n'y a pas de système d'interface pour ruby. Mais grâce à l'introspection, vous pouvez l'implémenter vous-même assez facilement. Voici un exemple simple qui peut être amélioré de nombreuses manières pour vous aider à démarrer:
La suppression d'une des méthodes déclarées sur Person ou la modification du nombre d'arguments lèvera un
NotImplementedError
.la source
Il n'existe pas d'interfaces à la manière Java. Mais il y a d'autres choses que vous pouvez apprécier en rubis.
Si vous souhaitez implémenter une sorte de types et d'interface - afin que les objets puissent être vérifiés s'ils ont des méthodes / messages dont vous avez besoin -, vous pouvez alors jeter un œil à rubycontracts . Il définit un mécanisme similaire aux PyProtocols . Un blog sur l'enregistrement de type ruby est ici .
Les abordés mentionnés ne sont pas des projets vivants, même si l'objectif semble être sympa au début, il semble que la plupart des développeurs ruby puissent vivre sans vérification de type stricte. Mais la flexibilité de ruby permet de mettre en œuvre la vérification de type.
Si vous voulez étendre des objets ou des classes (la même chose dans ruby) par certains comportements ou avoir quelque peu la manière ruby de l'héritage multiple, utilisez le mécanisme
include
ouextend
. Avec,include
vous pouvez inclure des méthodes d'une autre classe ou module dans un objet. Avecextend
vous pouvez ajouter un comportement à une classe, afin que ses instances aient les méthodes ajoutées. C'était une explication très courte cependant.Je pense que la meilleure façon de résoudre le besoin d'interface Java est de comprendre le modèle objet ruby (voir les conférences de Dave Thomas par exemple). Vous oublierez probablement les interfaces Java. Ou vous avez une application exceptionnelle sur votre emploi du temps.
la source
Comme de nombreuses réponses l'indiquent, il n'y a aucun moyen dans Ruby de forcer une classe à implémenter une méthode spécifique, en héritant d'une classe, y compris un module ou quelque chose de similaire. La raison en est probablement la prévalence du TDD dans la communauté Ruby, qui est une manière différente de définir l'interface - les tests spécifient non seulement les signatures des méthodes, mais aussi le comportement. Ainsi, si vous souhaitez implémenter une classe différente, qui implémente une interface déjà définie, vous devez vous assurer que tous les tests réussissent.
Habituellement, les tests sont définis de manière isolée à l'aide de simulacres et de stubs. Mais il existe aussi des outils comme Bogus , permettant de définir des tests contractuels. De tels tests définissent non seulement le comportement de la classe "primaire", mais vérifient également que les méthodes stubbed existent dans les classes coopérantes.
Si vous êtes vraiment préoccupé par les interfaces dans Ruby, je recommanderais d'utiliser un cadre de test qui implémente les tests de contrat.
la source
Tous les exemples ici sont intéressants mais il manque la validation du contrat d'interface, je veux dire si vous voulez que votre objet implémente toutes les définitions de méthodes d'interface et seulement celles-ci, vous ne pouvez pas. Je vous propose donc un exemple simple et rapide (peut être amélioré à coup sûr) pour vous assurer que vous avez exactement ce que vous attendez d'avoir via votre interface (le contrat).
considérez votre interface avec les méthodes définies comme ça
Ensuite, vous pouvez écrire un objet avec au moins le contrat Interface:
Vous pouvez appeler votre objet en toute sécurité via votre interface pour vous assurer que vous êtes exactement ce que l'interface définit
Et vous pouvez également vous assurer que votre Object implémente toutes vos définitions de méthodes d'interface
la source
J'ai prolongé un peu la réponse de carlosayam pour mes besoins supplémentaires. Cela ajoute quelques applications et options supplémentaires à la classe Interface:
required_variable
etoptional_variable
qui prend en charge une valeur par défaut.Je ne suis pas sûr que vous souhaitiez utiliser cette métaprogrammation avec quelque chose de trop grand.
Comme d'autres réponses l'ont indiqué, il vaut mieux écrire des tests qui appliquent correctement ce que vous recherchez, en particulier lorsque vous souhaitez commencer à appliquer des paramètres et à renvoyer des valeurs.
Attention, cette méthode ne génère une erreur que lors de l'appel du code. Des tests seraient toujours nécessaires pour une application correcte avant l'exécution.
Exemple de code
interface.rb
plugin.rb
J'ai utilisé la bibliothèque singleton pour le modèle donné que j'utilise. De cette façon, toutes les sous-classes héritent de la bibliothèque singleton lors de l'implémentation de cette "interface".
mon_plugin.rb
Pour mes besoins, cela nécessite que la classe implémentant "l'interface" la sous-classe.
la source
Ruby lui-même n'a pas d'équivalent exact aux interfaces en Java.
Cependant, comme une telle interface peut parfois être très utile, j'ai moi-même développé une gemme pour Ruby, qui émule les interfaces Java de manière très simple.
Ça s'appelle
class_interface
.Cela fonctionne tout simplement. Installez d'abord la gemme
gem install class_interface
ou ajoutez-la à votre Gemfile et rundbundle install
.Définition d'une interface:
Implémentation de cette interface:
Si vous n'implémentez pas une certaine constante ou méthode ou si le numéro de paramètre ne correspond pas, une erreur correspondante sera générée avant l'exécution du programme Ruby. Vous pouvez même déterminer le type des constantes en attribuant un type dans l'interface. Si nul, tout type est autorisé.
La méthode "implements" doit être appelée à la dernière ligne d'une classe, car c'est la position du code où les méthodes implémentées ci-dessus sont déjà vérifiées.
Plus d'informations sur: https://github.com/magynhard/class_interface
la source
J'ai réalisé que j'utilisais trop le modèle "Erreur non implémentée" pour les contrôles de sécurité sur les objets dont je voulais un comportement spécifique. J'ai fini par écrire un joyau qui permet essentiellement d'utiliser une interface comme celle-ci:
Il ne vérifie pas les arguments de méthode. Il fait à partir de la version0.2.0
. Exemple plus détaillé sur https://github.com/bluegod/rintla source