On Thu, Jul 04, 2013 at 01:59:30PM -0700, Buddy Burden wrote:
> Guys,
> 
> Okay, so if I want to create a class which fulfills the interface of
> another class, I would use inheritance.  But if I want to create a
> class which specifically does _not_ fulfill the interface of another
> class--let's say I want to create a [facade
> pattern|http://en.wikipedia.org/wiki/Facade_pattern]--then I use
> delegation instead.  So far so good.
> 
> But what aobut the same choice for roles?  Let's say I want to
> create an interface for something--let's say storing and loading an
> object.  Well, one choice is to use MooseX::Storage.  That's nice
> and simple, and (extra added bonus) already built for me.  But let's
> say that I have plans to move to a more complex back-end storage
> system one day (perhaps I'll want to store to a DB instead, or
> something along those lines).  It would behoove me, methinks, to
> hedge my bets by creating a simple API layer between my code and
> MooseX::Storage.  This will make it much simpler to swap out
> back-ends later.
> 
> This is a very common thing with classes, and I've done it many many
> times.  But I've never tried it with roles.
> 
> Now, if I compose the base role (in this case, MooseX::Storage) into
> my role (let's call it Persistent), then any classes which compose
> Persistent will also get the methods from MooseX::Storage.  That's
> like inheritance, and that's not what I want.  What I want is more
> like delegation, but obviously I can't quite do that: my Persistent
> role could have an attribute, sure, but it can't be an attribute of
> type MooseX::Storage, because you can't instantiate a role.
> 
> So my first thought is just to use `excludes` to "block" the
> MooseX::Storage methods from being composed into my classes.  But
> then I remembered [this 
> discussion|http://www.nntp.perl.org/group/perl.moose/2013/03/msg2590.html]
> which seemed to end with the conclusion that one _might_ need
> `alias` (_maybe_), but one should _never_ need `excludes` unless one
> is doing something wrong.(*)
> 
> So that tells me that there's probably a Better Way(tm) that I just
> don't know about, due to my inexperience with roles.  So I humbly
> request the greater wisdom of the Moosite community.  Please, show
> me the way. :-)
> 
> 
>               -- Buddy
> 
> 
> (*) Admittedly, not everyone agreed (particularly Ovid).  But that
> seemed the majority opinion.

There's nothing stopping you from using delegation in this case either.
All you have to do is make the object you delegate to consume both your
API role and MooseX::Storage, and then have the rest of your code just
use the API role. Using excludes is basically always the wrong answer.

For instance:

  package MyStorage;
  use Moose::Role;

  requires 'load', 'store';

  package MyPersistentThing;
  use Moose;

  # MooseX::Storage implements 'load' and 'store'
  with 'MyStorage', 'MooseX::Storage';

  # ...

  package MyClass;
  use Moose;

  has persistent_thing => (
      is      => 'ro',
      does    => 'MyStorage',
      handles => 'MyStorage', # this delegates only 'load' and 'store'
  );

-doy

Reply via email to