Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Bundling unrelated classes

by redlemon (Hermit)
on Dec 18, 2004 at 14:25 UTC ( #415859=perlquestion: print w/replies, xml ) Need Help??
redlemon has asked for the wisdom of the Perl Monks concerning the following question:

I don't know whether to post this as a request for wisdom or as a meditation. I chose for the first because it has a question at the end.

Anyway. I'm writing an chatbot. My design is modular: The bot has a connnection (aim/jabber/irc), a config, a brain, a scheduler, events, etc. All based on plugins so I can change parts while it is running.

The problem is that connections are callback based, and each callback only gets the connection object passed to it.

But the brain and the config need to be accessed from the callbacks.

The naive solution is to use globals, but I want to be able to use a test and a production bot at the same time. The other naive solution is to inherit the ChatBot class from Net::OSCAR (which is what I'm using for AIM) and stick the other objects into it. But that makes it hard to have both AIM and Jabber connections at the same time.

Hence I came up with Class::Bundle. The code is not really important here, if someone's interested let me know and I'll post it.

Class::Bundle is to be used as a base class.

package ChatBot; use base qw/Class::Bundle/; use ChatBot::Config; use ChatBot::Brain; use ChatBot::Logger; # exports logger() ... use Net::OSCAR; sub new { my($class, $name) = @_; my $self = bless { config => ChatBot::Config->new , brain => ChatBot::Brain->new , ... , oscar => Net::OSCAR->new } => ref($class) || $class; $self->bundle($name); # note 1) $self->inject('logger'); # note 2) }

  1. the bundle() method looks at $self and extracts all blessed members. It then goes and injects accessors for all members and the parent into each others namespaces;
  2. the inject() method injects the 'logger' method from the parent namespace into the child namespaces;

So, now the im_in() callback from Net::OSCAR gets the connection object ($oscar) passed to it and I can find my config by doing $oscar->config; and my parent node by $oscar->parent; (or  $oscar->whatever_name_I_passed_to_new();) etc.

This means that Class::Bundle is effectively grouping unrelated classes into a cooperating bundle.

Now, for the promised question(s):

  • Is this acceptable OO practice?
  • Is there a better way to do this? (I've been looking at Class::Decorator and such and at mixin but I couldn't see a way to use those.)

Replies are listed 'Best First'.
Re: Bundling unrelated classes
by simonm (Vicar) on Dec 18, 2004 at 18:55 UTC
    A design in which every object needs to be able to call methods on every other kind of object seems messy.

    A typical approach is to adopt a layered architecture, where the lower-level objects don't know much about the higher-level ones.

    For example, perhaps instead of having references to all of the other objects, the connection object can get by with just converting the incoming message into a standard format and passing it to a callback function? That way, some object from a higher layer could create the connection, pass it the relevant configuration information, and register the callback to be invoked when a message is received.

      It's not that I'm injecting all methods from all objects into each others namespaces, just that every object carries a reference to it's 'siblings' and 'parent'.

      But your suggestion of using my own callbacks is one I'll explore. thanks.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://415859]
Approved by BrowserUk
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (9)
As of 2018-05-24 12:59 GMT
Find Nodes?
    Voting Booth?