Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

RFC Bridge::Simple

by hypochrismutreefuzz (Scribe)
on Mar 09, 2008 at 17:25 UTC ( [id://673094]=perlmeditation: print w/replies, xml ) Need Help??

Bridge::Simple is a version of the Bridge and Adapter patterns in the Gang of Four Design Patterns book. In implementing the design pattern in Perl I altered the structure of the pattern subtly.

The source of the module

package Bridge::Simple; use strict; use Carp; sub new { my $class = shift; my $object_ref = shift; my $method_map = shift; my $self = {}; $self->{$_} = $method_map->{$_} for keys %$method_map; $self->{object} = $object_ref; return bless($self, $class); } sub AUTOLOAD { my $self = shift; use vars '$AUTOLOAD'; $AUTOLOAD =~ s/.*:://; croak "no method $AUTOLOAD" unless exists $self->{$AUTOLOAD}; my $method = $self->{$AUTOLOAD}; return $self->{object}->$method(@_); } 1;

A structural diagram of the Bridge pattern for an implementation of a cgi Session object that supports storage of the state of a session:

---------- | Session | | object | ---------- |obj ref: | | $store | ----------------- | \___|____\| Bridge::Simple | | Database| | | /| | | objects:| interface: | $map $obj --------------->| DB_File,| fetch() ---:------------ | SQLite | fetch_ids() : store() : delete() : : ----------- (maps the interface of the using object to the implementation interface, eg, the DB_File implementation) { fetch=>'get_value', fetch_ids=>'get_keys', store=>'set_value', delete=>'delete', }

The Bridge pattern allows using different storage methods. I have tested with SQLite and DB_File based storage.

Client code would construct the bridge to the database module, in this case a DB_File based interface:

my $storage = Bridge::Simple->new( Database::DB_File->new( type=>'db_btree', dbpath=>$path_info{sessionpath}, dbname=>'sessiontree.db', ), $storage_map, );

An adapter would map an interface to an already existing module, such as serializing data:

my $xml_map = { serialize=>'xml_out', deserialize=>'xml_in', }; my $yaml_map = { serialize=>'Dump', deserialize=>'Load', }; my $xml_serializer = Bridge::Simple->new( XML::Simple->new(), $xml_map, ); my $yaml_serializer = Bridge::Simple->new( YAML->new(), $yaml_map, );

Replies are listed 'Best First'.
Re: RFC Bridge::Simple
by peter (Sexton) on Mar 09, 2008 at 17:43 UTC

    Calling the 'object' method on the bridge object would result in strange problems.

    Peter Stuifzand
Re: RFC Bridge::Simple
by plobsing (Friar) on Mar 09, 2008 at 22:40 UTC
    As peter hinted, you are storing 2 independent things in the same place. I would suggest you either store the object and method map in separate slots of an array or hash, or curry the object into the methods in the method map.

    I prefer the latter because it makes the structure of the bridge object simpler.
    sub curry_method { my ($obj, $meth) = @_; sub { $obj->$meth(@_) }; }

      You make a very good point about separating the 'name spaces' for the object's attribute storage and the method map.

      Perhaps I am still half-asleep, but I think the curry method routine is wrong. I think you need to shift the object and method values off @_. Otherwise $meth will be called with args that look like: ($obj, $obj, $meth, ... ).

      sub _curry_method { my $object = shift; my $method = shift; return sub { $object->method(@_) }; }

      I wonder if it makes sense to do this. I believe you wind up with a new closure for each method called on every instance of your bridge class. When I'm more awake I need to research how Perl handles garbage collection of code refs, and test to see if there any differences in memory or speed for this approach versus an AUTOLOADED method call.

      Update:Yep, I was half asleep. plobsing's correct, the my ($o, $m) = @_; is fine. I blame it on Daylight Savings Time... I wasn't keeping the various meanings of @_ straight.


      TGI says moo

        You have to keep the name space of a bridge clear, so I would inline the curry_method function (alternatively, you could keep it in some other namespace). However, here's a simple case demonstrating that slurping @_ works:
        $ perl -MCGI -e 'sub curry_method{ my ($o, $m) = @_; sub { $o->$m(@_) } } $c = CGI->new; $p = curry_method($c, 'p'); print $p->("Hello World"), "\n"' <p>Hello World</p>
        You make a good point about having a closure for every method of every instance of the bridge class. Its a little extra space for a little extra simplicity. I prefer simplicity to speed/space in this case.

        The doubled $obj in @_ only occurs if you are calling the thing like a method twice. Once the object is curried, the method becomes a plain old function. I envisioned something like this (untested):
        package Bridge::Simple; use strict; use Carp; sub new { my ($class, $obj, $mapping) = @_; my $self = {}; @{$self}{keys %$mapping} = map { my $meth = $_; sub { $obj->$meth(@_) } } values %$mapping; bless $self, $class; } sub AUTOLOAD { my $self = shift; our $AUTOLOAD =~ s/.*:://; croak "no method $AUTOLOAD" unless exists $self->{$AUTOLOAD}; goto $self->{$AUTOLOAD}; } 1;

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://673094]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (1)
As of 2024-04-25 00:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found