On Thu, 10 Apr 2014, Thalhammer, Jeffrey Ryan wrote:

package FooRole;
use Moose::Role;
requires qw(open close read write);
requires qw(eenie meenie);

package FooWrapper;
use Moose;

has delegate => (
    is => 'ro',
    isa => 'Foo',
    handles => [ qw(open close read write) ],
    required => 1,
);

sub eenie {...}
sub meenie {...}

with qw(FooRole);

That only works if the with comes after the delegation has been established. 
The trouble is, I
only want to write that list qw(open close read write) once.

Why write it once?  Your interface needs to declare it, and your implementation 
needs to provide it - the implementation may for example declare/provide more 
than the interface requires.

Have you considered encapsulating your Foo delegate into an implementation 
role, rather than declaring it in the class, and then consume the 
implementation and interface roles in the right order?  The class doesn't 
really need to know about the implementation, only the interface encapsulating 
1..N implementations.

@FooRole::METHODS_TO_DELEGATE.

I'm not a fan of centralising/exposing constants like this - it's breaking OO 
encapsulation.  A method returning this info would be a better solution if you 
need to go this way.  This constant-centric lookup-table approach drives me to 
tears in the legacy Perl code that I work on - it seems like a good idea at the 
time, but you're splaying your implementation across N packages/classes/files 
which has a real cost in terms of readability and maintainability, and I don't 
think 'but we only need to update it in 1 place' really makes all that much 
sense given its inherent risk and cost.  But YMMV, just another IMHO :-)

You could argue that I shouldn’t do this at all, because FooRole shouldn’t know 
anything about
how the required methods will be implemented. And if I really want to commit to 
a particular
implementation (like using the delegate to handle qw(open close read write) for 
example) then I
should just put that in the role directly.

If you're contemplating swapping out implementations, I'd consider 
encapsulating each implementation behind a set of Interface method names, 
rather than coupling your interface to any particular implementation.  So none 
of your client code needs to change when you swap in an alternate 
implementation (say via configuration, or at runtime) and things should still 
just work.

It's all an area that I'm still exploring myself, so hopefully I haven't made 
any obvious errors above as this is all from memory and I haven't looked at 
this for a few months now.

--
Niall Young
ni...@iinet.net.au

Reply via email to