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