2010-08-16 7 views
16

Je suis nouveau à Ruby (expérimenté avec Python, C++ et C). J'ai besoin de créer une classe qui ne doit être utilisée que par d'autres classes et méthodes dans un module. En Python, je l'appellerais simplement __classname. J'utiliserais un typedef vide en C++. Comment est-ce que je fais ceci dans Ruby (ou suis-je aboyant le mauvais arbre et ne pas le faire le "chemin de Ruby"?)Classe privée (méthode non classe) dans un module Ruby?

Répondre

4

Je n'ai pas vu un tel concept jusqu'ici dans Ruby, mais je suppose que vous pourriez simuler cela en créant une méthode privée qui retournerait une classe créée en tant que variable locale (souvenez-vous que dans Ruby, une classe est un objet comme n'importe quel autre, et peut être instanciée dans une méthode et retournée par elle). Par ailleurs, même les méthodes privées dans Ruby ne sont pas aussi privées que dans d'autres langues - vous pouvez toujours y accéder en utilisant la méthode send. Mais faire cela implique que vous sachiez ce que vous faites.

+0

Bon sang. Je pensais que c'était possible, mais j'espérais que ce ne serait pas hackish ... Oh bien. Je suppose que je trouverai une façon plus Ruby-ish de le faire. Merci cependant - vous avez répondu à ma question. – c4757p

+0

Vous devriez attendre d'autres réponses, je peux aussi bien me tromper. BTW, qu'est-ce que vous voulez atteindre avec des cours privés? Oh, une autre chose: les variables d'instance sont toujours _private_ dans Ruby, et afin de les exposer, vous devez créer des méthodes accesseur, mais personne ne considère que _hackish_. :) –

+0

En fait, je pense que faire ça/est un peu hackish - c'est l'une des choses que je n'aime pas chez Ruby. L'autre est l'appel de fonction implicite stupide - je souhaite vraiment que je pourrais simplement se référer à une fonction par son nom sans l'appeler, sans utiliser de symbole ou de lambda. Je m'interface dynamiquement avec une bibliothèque externe en utilisant FFI, et je voulais garder les structures FFI C 'loin du module utilisable. – c4757p

27

La chose la plus importante à réaliser est qu'une classe n'a rien de spécial. C'est juste un objet. Par convention, les classes sont affectées à des constantes, mais rien ne dit qu'elles ont comme. Et comme les classes ne sont que des objets comme tout autre objet, vous les rendez privés de la même manière que tout autre objet privé.

Voici les possibilités que je peux penser, dans l'ordre de plus en plus privateness:

  1. Il suffit de les emboîter dans un espace de noms (module à savoir). Dans Ruby, on s'attend généralement à ce que tous les modules et classes d'une bibliothèque vivent dans un espace de noms portant le même nom que la bibliothèque (my_awesome_libraryMyAwesomeLibrary), mais en général, tout ce qui est imbriqué sous cet espace de noms est considéré comme privé. En fait, en plus de Test::Unit::TestCase, je ne peux pas penser à un seul exemple d'un espace de noms profond à trois niveaux qui devrait être utilisé par le code client.
  2. même que 1., mais nommez quelque chose d'évident, comme MyAwesomeLibrary::Internal::FfiStruct
  3. même que 1. ou 2., et le marquer avec l'étiquette :nodoc: RDoc.
  4. similaire à 3., mais utilisez un système de documentation plus moderne comme YARD, qui vous permet en fait de marquer explicitement les API privées.
  5. Utilisez une méthode au lieu d'une constante. Les méthodes peuvent être rendues privées. (Par souci de cohérence, vous pouvez faire en sorte que le nom de la méthode commence par une lettre majuscule, pour la faire ressembler à une constante.) snake_case est une convention. Ils sont toujours privés. Notez que les méthodes privées et les variables d'instance peuvent être accédées de manière triviale à l'aide de la réflexion. send semble être plus répandu que instance_variable_get, ce qui explique pourquoi je considère que les variables d'instance ont un niveau de confidentialité plus élevé que les méthodes. En réalité, le seul moyen d'obtenir une confidentialité ou une encapsulation réelle est d'utiliser des variables et des fermetures locales. Notez toutefois que cela peut vous empêcher d'utiliser la syntaxe de définition de module, de classe ou de méthode de Ruby, car celles-ci créent de nouvelles étendues. Dans tous les cas où vous avez besoin d'accéder à la classe, vous devez utiliser Module.new, Class.new ou Module#define_method.

Ex.:

module MyAwesomeLibrary 
    struct = Class.new(FFI::Struct) do 
    # ... 
    end 

    PublicInterface = Class.new do 
    define_method(:initialize) do |bar| 
     @foo = struct.new(bar) 
    end 
    end 
end 

Et oui, c'est la seule façon de parvenir à une véritable informations 100% cacher et l'encapsulation dans Ruby. Cependant, la manière normale de Ruby serait de documenter simplement le contenu comme étant privé (peut-être le pousser vers le bas d'un niveau de nom) et de faire confiance à vos collègues développeurs. Dans la communauté Ruby, ceci est parfois résumé sous le slogan Python "Nous sommes tous des adultes consentants".

+0

"Nous sommes tous des adultes consentants" est un slogan de la communauté Python, pas de la communauté Ruby. –

+0

Je ne voulais pas laisser entendre qu'il appartient à la communauté Ruby, mais seulement qu'il y est utilisé. Mais vous avez raison, le libellé est trompeur. Vingt ans d'apprentissage de l'anglais ne suffisent apparemment pas :-) –

+0

Bonne réponse, merci! –

10

Prenant ces informations directement à partir this blog post, mais depuis Ruby 1.9.3, vous pouvez créer une classe privée dans un module en utilisant private_constant:

class Person 
    class Secret 
    def to_s 
     "1234vW74X&" 
    end 
    end 
    private_constant :Secret 

    def show_secret 
    Secret.new.to_s 
    end 
end 
Questions connexes