Je sais très bien quand utiliser des sous-classes et des modules, mais plus récemment, j'ai vu des classes imbriquées comme celle-ci:
class Foo
class Bar
# do some useful things
end
end
Ainsi que des classes imbriquées dans des modules tels que:
module Baz
class Quux
# more code
end
end
La documentation et les articles sont rares ou je ne suis pas suffisamment renseigné sur le sujet pour trouver les termes de recherche appropriés, mais je n'arrive pas à trouver beaucoup d'informations sur le sujet.
Quelqu'un pourrait-il fournir des exemples ou des liens vers des publications sur pourquoi/quand ces techniques seraient utilisées?
Other OOP) a classes internes qui ne peuvent être instanciées sans être liées à une classe de niveau supérieur. Par exemple, en Java,
class Car {
class Wheel { }
}
seules les méthodes de la classe Car
peuvent créer Wheel
s.
Ruby n’a pas ce comportement.
En rubis,
class Car
class Wheel
end
end
diffère de
class Car
end
class Wheel
end
seulement au nom de la classe Wheel
vs. Car::Wheel
. Cette différence de nom peut expliquer aux programmeurs que le Car::Wheel
La classe ne peut représenter qu'une roue de voiture, par opposition à une roue générale. L'imbrication des définitions de classe dans Ruby est une question de préférence, mais elle a une fonction en ce sens qu'elle impose plus fortement un contrat entre les deux classes et transmet ainsi plus d'informations à leur sujet et à leur nature. les usages.
Mais pour l’interprète Ruby, ce n’est qu’une différence de nom.
En ce qui concerne votre deuxième observation, les classes imbriquées dans les modules sont généralement utilisées pour nommer les classes en espace. Par exemple:
module ActiveRecord
class Base
end
end
diffère de
module ActionMailer
class Base
end
end
Bien que ce ne soit pas la seule utilisation de classes imbriquées à l'intérieur de modules, c'est généralement la plus courante.
En Ruby, définir une classe imbriquée est similaire à définir une classe dans un module. En réalité, il ne force pas l'association entre les classes, il crée simplement un espace de noms pour les constantes. (Les noms de classe et de module sont des constantes.)
La réponse acceptée n'était correcte à propos de rien.1 Dans l'exemple ci-dessous, je crée une instance de la classe lexicalement fermée sans qu'une instance de la classe englobante n'ait jamais existé.
class A; class B; end; end
A::B.new
Les avantages sont les mêmes que ceux des modules: encapsulation, code de regroupement utilisé à un seul endroit et placement du code plus près de l'endroit où il est utilisé. Un grand projet peut avoir un module externe qui se répète dans chaque fichier source et contient beaucoup de définitions de classe. Lorsque les divers cadres et codes de bibliothèque remplissent tous ces fonctions, ils ne contribuent qu’un seul nom au niveau supérieur, ce qui réduit les risques de conflits. Prosaic, bien sûr, mais c'est pourquoi ils sont utilisés.
L'utilisation d'une classe au lieu d'un module pour définir l'espace de noms externe peut avoir un sens dans un programme ou un script d'un fichier, ou si vous utilisez déjà la classe de niveau supérieur pour quelque chose, ou si vous allez réellement ajouter du code pour lier les classes entre elles. en vrai classe intérieure style. Ruby n'a pas de classes internes, mais rien ne vous empêche de créer à peu près le même comportement dans le code. Pour référencer les objets externes à partir des objets internes, il vous faudra tout de même être pointés de l'instance de l'objet externe, mais imbriquer les classes suggérera que c'est ce que vous êtes en train de faire. Un programme soigneusement modélisé peut toujours créer d'abord les classes englobantes, et elles peuvent être raisonnablement décomposées avec des classes imbriquées ou internes. Vous ne pouvez pas appeler new
sur un module.
Vous pouvez utiliser le modèle général même pour les scripts, où l'espace de noms n'est pas vraiment nécessaire, juste pour le plaisir et la pratique ...
#!/usr/bin/env Ruby
class A
class Realwork_A
...
end
class Realwork_B
...
end
def run
...
end
self
end.new.run
Vous voudrez probablement l'utiliser pour grouper vos classes dans un module. Une sorte de chose d'espace de noms.
par exemple, Twitter Twitter utilise les espaces de noms pour y parvenir:
Twitter::Client.new
Twitter::Search.new
Ainsi, les deux classes Client
et Search
résident dans le module Twitter
.
Si vous voulez vérifier les sources, vous pouvez trouver le code des deux classes: ici et ici .
J'espère que cela t'aides!
Il y a encore une autre différence entre les classes imbriquées et les modules imbriqués dans Ruby avant la version 2.5, mais d'autres réponses n'ont pas été couvertes, ce qui, à mon avis, doit être mentionné ici. Il s'agit du processus de recherche.
En bref: en raison de la recherche constante de niveau supérieur dans Ruby avant la version 2.5, Ruby peut finir par chercher votre classe imbriquée dans le mauvais place (dans Object
en particulier) si vous utilisez des classes imbriquées.
Dans Ruby avant 2.5:
Structure de classe imbriquée: Supposons que vous ayez une classe X
, avec la classe imbriquée Y
ou X::Y
. Et puis vous avez une classe de niveau supérieur nommée aussi Y
. Si X::Y
n'est pas chargé, la suite se produit lorsque vous appelez X::Y
:
N'ayant pas trouvé Y
dans X
, Ruby essaiera de le rechercher dans les ancêtres de X
. Depuis X
est une classe et non un module, il a des ancêtres, parmi lesquels sont [Object, Kernel, BasicObject]
. Donc, il essaie de rechercher Y
dans Object
, où il le trouve avec succès.
Pourtant, il s’agit du niveau supérieur Y
et non pas X::Y
. Vous obtiendrez cet avertissement:
warning: toplevel constant Y referenced by X::Y
Structure de module imbriquée: Supposons que dans l'exemple précédent, X
soit un module et non une classe.
Un module a seulement comme ancêtre: X.ancestors
produirait [X]
.
Dans ce cas, Ruby ne pourra pas rechercher Y
dans l'un des ancêtres de X
et jettera un NameError
. Rails (ou tout autre framework avec autoloading) essaiera de charger X::Y
après ça.
Voir cet article pour plus d'informations: https://blog.jetbrains.com/Ruby/2017/03/why-you-should-not-use-a-class-as-a- namespace-in-Rails-applications /
Dans Ruby 2.5:
La recherche constante de niveau supérieur a été supprimée.
Vous pouvez utiliser des classes imbriquées sans craindre de rencontrer ce bogue.
En plus des réponses précédentes: Module in Ruby est une classe
$ irb
> module Some end
=> nil
> Some.class
=> Module
> Module.superclass
=> Object