2009-11-10 5 views
1

J'ai un journal de texte d'un jeu avec (par exemple) deux types d'entrées à savoir. Chat et événement. Pour la plupart, ils sont très similaires, donc j'ai une classe LogEntry définie comme telle; LogChat et LogEvent étendent LogEntry et effectuent un traitement supplémentaire en rapport avec leur domaine. Tout fonctionne comme prévu;self = Descendant dans Ruby?

chat = LogEntry.new("some chat") 
event = LogEntry.new("some event") 

chat.parse.class # => LogChat 
event.parse.class # => LogEvent 

Question: La méthode de classe LogEntry.parse renvoie essentiellement une entrée analysable de la classe appropriée. Dans ce contexte, l'entrée analysée est le bit important. Mais nous pourrions renommer la méthode d'instance 'parse' en 'what_type_should_i_be?'. Je veux que l'objet agisse sur cette information et que 'self.become LogEntry.parse (self)'

Maintenant, pour analyser une entrée, je dois le faire; Je veux pousser plus loin afin que j'obtienne le même résultat avec;

entry.parse 

J'ai essayé l'évidence; J'ai l'erreur Can't change the value of self. Est-ce que quelqu'un sait comment je devrais y arriver?

Éditer: J'ai changé mes exemples parce que de nombreuses réponses se concentraient sur l'itération sur de nombreuses entrées. La réponse de Chuck montre élégamment que cette situation n'est pas un problème.

Dans le cas où cela susciterait l'intérêt de quelqu'un, j'ai trébuché sur Evil Ruby qui vous permet de vous mêler de `self.class '. Il y a un joli article d'Orielly à ce sujet qui s'appelle Ruby code that will swallow your soul! Je suis en train de regarder pour voir s'il offre des réponses. (Edit: evil.rb est bien nommé! Quelque chose de bas niveau ne semble pas approprié pour une distribution stable/à long terme.)

Répondre

2

Je pense que le problème fondamental est que each est la mauvaise méthode ici. Soit parse modifiez l'état interne de l'objet plutôt que l'objet lui-même, ou utilisez map! pour remplacer les objets de la collection par les nouvelles versions.

entries.map! {|entry| entry.parse} 

mettra à jour les objets du tableau avec le résultat de l'appel parse sur eux, donc il n'y a aucune raison de faire des choses bizarres à self dans la méthode parse.

+0

+1 pour commencer un intéressant train de pensées. Je vais voir si je peux implémenter ceci dans la méthode 'parse'. Je voudrais que l'objet se regarde et dise "en fait, je suis sur ces types d'objets." – deau

+0

Pensez-vous que je fais des choses étranges à moi-même? Théoriquement, est-il dangereux d'avoir un objet qui se contrôle de cette manière? – deau

+1

Eh bien, il est techniquement impossible de se définir soi-même sans un piratage de très bas niveau, ce que je voulais dire par "faire des trucs bizarres à soi-même". Un objet ne peut pas changer sa propre identité (même si vous pouviez définir 'self' dans une méthode, cela ne changerait pas l'identité de l'objet en dehors de la méthode - les variables ne sont que des références). Un objet ne peut contrôler que son état interne, c'est pourquoi j'ai recommandé de représenter la chose qui peut changer de cette façon. – Chuck

0

Pour les débutants, vos commentaires indiquent que LogEntry.parse renvoie un objet LogChat ou LogEvent. Donc vous demandez à l'objet de se changer en un type d'objet différent.

Il ressemble également à des méthodes de classe et les méthodes d'instance sont en cours confus un peu je devine un peu, mais pourquoi ne pourriez-vous faire:

entries.each do |entry| 
    some_type_log = entry.parse 
    some_type_of_log.save! 
end 

EDIT: désolé, a voulu clarifier quelque chose. Puisque vous analysez des données qui font partie de LogEntry et que vous voulez qu'une entrée soit analysée, il n'est pas nécessaire de passer des paramètres. Gardez simplement la méthode d'analyse sans paramètre.

Si vous savez quel type de quelque chose de journal est, vous pouvez sauter une étape et analyser sur la façon chat_entry = LogChat.new. (: Log => logentry)

puis faire une méthode appelée journal qui est votre analyseur qui gère explicitement les éléments liés au chat.

+0

Merci pour votre temps m8, mais je ne confond pas les méthodes instance/classe; La méthode de classe 'LogEntry.parse' provoque une cascade le long des méthodes progressivement plus spécifiques' EntryType.parse'. Le résultat est un objet d'entrée entièrement analysé et catégorisé (par classe). La méthode d'instance parse appelle la méthode de classe dans LogEntry, déclenchant une nouvelle cascade, de sorte que vous puissiez recalculer des entrées déjà analysées. Votre solution est bien et semblable à ce que j'ai déjà en place (voir la question!) – deau

0

Vous avez une certaine confusion chaîne/tableau/logentry, mais en supposant que vous obtenez ça a fonctionné, et à la fin vous voulez toujours avoir une sous-classe Array remplaçant son propre contenu, vous devez utiliser replace:

self.replace(LogEntry.parse(self)) 
+0

malheureusement remplacer ne fera que copier le contenu de la matrice. Les sous-classes de LogEntry ont des variables d'instance (et une classe ofcourse!) Qui ne sont pas couvertes par le remplacement. – deau

2

Si vous pouvez sortir la fonctionnalité en différents modules, vous pouvez muter extend()self que vous aimez:

class LogEntry 
    ... 
    def parse!  # This mutates self! 
    case LogEntry.parse! 
    when :chat 
     self.extend MyApp::LogChat 
    when :event 
     self.extend MyApp::LogEvent 
    else 
     raise MyApp::Exception, "waaah" 
    end 
    end 
end 

Vous ne devez pas faire une déclaration case avec maladroits appels répétés à self.extend(), bien sûr, mais vous avez l'idée.

+0

Tout à fait raison. Je pourrais simuler le tout en utilisant 'self. @ Type' au lieu de' self.class'. Mixins pourrait ajouter les fonctionnalités pertinentes et les variables d'instance. Je vais devoir y réfléchir un peu plus. – deau

Questions connexes