Upgrade to Pro — share decks privately, control downloads, hide ads and more …

(Parameterized) Roles

(Parameterized) Roles

Roles are an excellent object-oriented tool both for allomorphism and for reuse.

Roles facilitate allomorphism by favoring "does this object do X" versus "is this object a subclass of X". You often care more about capability than inheritance. In a sense, roles encode types better than inheritance.

Roles also provide an excellent faculty for reuse. This effectively eliminates multiple inheritance, which is often the only solution for sharing code between unrelated classes.

Roles can combine with conflict detection. This eliminates accidental shadowing of methods that is painful with multiple inheritance and mixins.

Parameterized roles (via MooseX::Role::Parameterized) improve the reusability of roles by letting each consumer cater the role to its needs. This does sacrifice some allomorphism, but there are ways to restore it.

Shawn Moore

October 23, 2011
Tweet

More Decks by Shawn Moore

Other Decks in Programming

Transcript

  1. (Parameterized) Roles Shawn M Moore Best Practical Solutions http://sartak.org Friday,

    September 11, 2009 Presented YAPC::Asia, 2009-09-11. Tokyo Institute of Technology, Tokyo, Japan.
  2. "The more I use roles, the less I understand why

    anyone would want to use the inheritance model." - Ovid "A role-aware type system allows you to express yourself with better genericity." - chromatic "Roles are the sound of diamond inheritance that people have stopped banging their heads against." - hdp ࠷ۙ࿩୊ͷʮϩʔϧ(໾ׂ)ʯ ྲྀߦΔͷʹ͸ཧ༝͕͋Γ·͢ Friday, September 11, 2009 You've probably heard a lot about this new feature called "roles" lately. Not only are a lot of people talking about roles, but a lot of people are using roles. And for good reason!
  3. Example Role package Counter; use Moose::Role; has counter => (

    is => 'ro', isa => 'Int', default => 0, ); sub increment { my $self = shift; $self->counter($self->counter + 1); } ϩʔϧͷղઆΛ͢Δલʹɺ ·ͣ͸αϯϓϧΛ͓ݟͤ͠·͢ Friday, September 11, 2009 Before I get into what a role is, and why roles are awesome, I want to just dive into what a role looks like.
  4. Example Role package Counter; use Moose::Role; has counter => (

    is => 'ro', isa => 'Int', default => 0, ); sub increment { my $self = shift; $self->counter($self->counter + 1); } useจ͕ҟͳΔ͚ͩͰɺMooseͷ Ϋϥεʹͦͬ͘ΓͰ͢Ͷ Friday, September 11, 2009 You might notice a strong resemblance to regular Moose code. The only difference is that we "use Moose::Role" instead of "use Moose". This means that this package represents a role instead of a class. Oh, damnit, wait a second.
  5. Example Role use MooseX::Declare; role Counter { has counter =>

    ( is => 'ro', isa => 'Int', default => 0, ); method increment { $self->counter($self->counter + 1); } } ΋ͬͱ͖ͬ͢Γͤͯ͞Έ·ͨ͠ Friday, September 11, 2009 Much better. Anyway, so we have a role named Counter with an attribute and an increment method. Roles are not classes, so we can't call Counter->new. A role is more just a container for methods and attributes.
  6. Consuming a Role class Odometer with Counter { method reset(Crook

    $you) { $you->break_into($self); $self->counter(0); $you->plead('innocent'); } } ϩʔϧ͸withΛ࢖ͬͯ૊ΈࠐΈ·͢ Friday, September 11, 2009 This is what consuming a role looks like. Here we have a class that needs a counter for keeping track of how many miles you have driven. "with" is the key word for consuming a role, much like "extends" is the key word for subclassing.
  7. Consuming a Role class Odometer with Counter { method reset(Crook

    $you) { $you->break_into($self); $self->counter(0); $you->plead('innocent'); } } ͜ΕͰϩʔϧͷcounter ΞτϦϏϡʔτ͕௥Ճ͞Ε·ͨ͠ Friday, September 11, 2009 As you can see here, the role has added the counter attribute to Odometer. It also gained the increment method, which is presumably called by other parts of the system.
  8. Consuming a Role class Odometer extends Widget with Counter {

    method reset(Crook $you) { $you->break_into($self); $self->counter(0); $you->plead('innocent'); } } ϩʔϧͱܧঝ͸ҧ͏ͱ͍͏͜ͱʹ͝஫ҙ͍ͩ͘͞ Friday, September 11, 2009 I must stress that consuming a role is not inheritance. You can still use inheritance alongside roles. What roles do are "flatten" its bits into the class.
  9. class Odometer extends Widget { has counter => ( is

    => 'ro', isa => 'Int', default => 0, ); method increment { $self->counter($self->counter + 1); } method reset(Crook $you) { $you->break_into($self); $self->counter(0); $you->plead('innocent'); } } ϩʔϧ͸࣮ߦ࣌ʹίϐʔˍϖʔετ͢ΔΑ͏ͳ΋ͷͰ͢ Friday, September 11, 2009 The flattening is a lot like copying and pasting the code from the role into its consumer. That's even a pretty good description of what happens behind the scenes, though it doesn't happen at the strings-of-code level.
  10. Class Building Blocks class Action::Throw with Action::Role::Direction with Action::Role::Item {

    … } class Action::Melee with Action::Role::Monster with Action::Role::Direction { … } ϩʔϧͷΑ͞͸࠶ར༻ੑʹ͋Γ·͢ Friday, September 11, 2009 My favorite feature of roles is their reusability. One way to look at roles is that they are class building blocks.
  11. Class Building Blocks class Action::Throw with Action::Role::Direction with Action::Role::Item {

    … } class Action::Melee with Action::Role::Monster with Action::Role::Direction { … } ͲͪΒ΋ํ޲ؔ܎ͷ໾ׂΛ࣋ͭ͜ͱ͕Θ͔Γ·͢Ͷ Friday, September 11, 2009 If we factor out common behavior, we get to name that chunk of behavior, and reuse all that code. We know that these two actions have something to do with direction.
  12. Class Building Blocks class Action::Throw extends Action::Direction extends Action::Item {

    … } class Action::Melee extends Action::Monster extends Action::Direction { … } ଟॏܧঝ͕Α͘ͳ͍ͷ͸͝ଘ͡Ͱ͢ΑͶʁͶʁ Friday, September 11, 2009 You might be wondering how any of this is better than multiple inheritance. Surely you know multiple inheritance is evil, right? RIGHT? By the way, did anyone here even know how to use multiple inheritance with MooseX::Declare? I sure didn't until I wrote this slide.
  13. class Action::Monster { has monster => (…); method name {

    $self->monster->name } } Multiple Inheritance ଟॏܧঝ͕Α͘ͳ͍ྫΛ঺հ͠·͠ΐ͏ Friday, September 11, 2009 Let's look at an example of why multiple inheritance is maligned. Here we have a class with a "name" method.
  14. class Action::Direction { has direction => (…); } Multiple Inheritance

    ͪ͜ΒͷΫϥεʹ͸direction͔͋͠Γ·ͤΜ Friday, September 11, 2009 Now we have another class. It just has a direction.
  15. class Action::Melee extends Action::Direction extends Action::Monster { … } Multiple

    Inheritance ͍·͸྆ํܧঝͯ͠΋໰୊͋Γ·ͤΜ͕… Friday, September 11, 2009 And now we have a class that inherits from both of those. So everything is fine right now. But at some point the requirements change.
  16. class Action::Direction { has direction => (…); method name {

    $self->direction->name } } Multiple Inheritance ͜͜ʹnameϝιου͕ඞཁʹͳͬͨ৔߹ Friday, September 11, 2009 We need a "name" method in Action::Direction for some other class.
  17. $melee->name Multiple Inheritance Կͷܯࠂ΋ͳ͘nameͷҙຯ͕มΘͬͯ͠·͍·͢ Friday, September 11, 2009 This used

    to return monster name. Now this has changed without warning to the direction name, because Action::Direction is the leftmost parent. I hope your tests are very thorough! This is a huge pain to debug. Code reuse and cleanliness is often not worth this pain, so we avoid multiple inheritance.
  18. class Action::Melee with Action::Direction with Action::Monster { … } Multiple

    Roles ϩʔϧͷ৔߹͸ܧঝͱ͸ҧͬͯ Friday, September 11, 2009 If these were roles instead, something very different happens.
  19. Due to a method name conflict in roles 'Action::Direction' and

    'Action::Monster', the method 'name' must be implemented or excluded by 'Action::Melee' Multiple Roles Moose͕͜ͷΑ͏ͳΤϥʔΛग़ͯ͘͠Ε·͢ Friday, September 11, 2009 Once we add the "name" method to Direction, we get this error. I'll talk about what it means soon, but one nice thing about this error is Moose throws it..
  20. AT COMPILE TIME! ͔͠΋ɺίϯύΠϧ࣌ʹ! Friday, September 11, 2009 ...immediately. You

    don't have to wait for the class to be instantiated, or the "name" method to be called. Moose throws conflict error at "with" time.
  21. class Action::Melee with Action::Direction with Action::Monster { … } Conflict

    Resolution ΋ͬͱ΋ɺ͜ͷΤϥʔΛճආ͢Δํ๏΋͋Γ·͢ Friday, September 11, 2009 So this code is an error. But as alluded to in the error message, we can resolve this conflict. There are several ways to do this.
  22. class Action::Melee with Action::Direction with Action::Monster { method name {

    "melee" } } Conflict Resolution ΫϥεʹϝιουΛ ఆٛͯ͠͠·͏ͷ͸ͻͱͭͷखͰ͢ Friday, September 11, 2009 The class could define its own method. When a class defines a method and a role it consumes defines the same method, the class wins. So this resolves the conflict by overriding the "name" methods from the two roles. It's kind of like plugging your ears and yelling that everything is okay. But sometimes that really is all you need.
  23. class Action::Melee with Action::Direction with Action::Monster excludes name { …

    } Conflict Resolution িಥ͢ΔϝιουΛ૊Έࠐ·ͳ͍ख΋͋Γ·͢ Friday, September 11, 2009 Another option is to exclude one of the conflicting methods. This way, Direction's "name" method is the one that is added to Melee. Obviously you can only use this where it makes sense. It probably doesn't make sense to use it in this example. XXX: This syntax doesn't actually work yet as of 2009-08-22. A failing test has been submitted!
  24. class Action::Melee with Action::Direction alias { name => 'direction_name' }

    with Action::Monster alias { name => 'monster_name' } { method name { loc '%1 (at %2)', $self->monster_name, $self->direction_name; } } 2ͭͷϝιουΛ݁߹ͯ͠͠·͏ख΋͋Γ·͢ Friday, September 11, 2009 Another option is to combine the two methods. Here we disambiguate the two conflicting "name" methods, then use them in our own "name" method which serves both roles well enough. I've found that this is usually the best solution.
  25. Due to a method name conflict in roles 'Action::Direction' and

    'Action::Monster', the method 'name' must be implemented or excluded by 'Action::Melee' Conflict Resolution ͜ͷΤϥʔͷҙຯ͸Θ͔Γ·ͨ͠Ͷ Friday, September 11, 2009 So now we know what this error message means.
  26. Due to a method name conflict in roles 'Action::Direction' and

    'Action::Monster', the method 'name' must be implemented or excluded by 'Action::Melee' Conflict Resolution িಥͨ͠ϝιου͸ΦʔόʔϥΠυͰ͖·͢ Friday, September 11, 2009 You can override the conflicting methods in the class, with or without reusing each conflicting method.
  27. Due to a method name conflict in roles 'Action::Direction' and

    'Action::Monster', the method 'name' must be implemented or excluded by 'Action::Melee' Conflict Resolution ෆཁͳํΛഉআͯ͠΋͔·͍·ͤΜ Friday, September 11, 2009 Or, if it makes sense, just exclude one of the methods so that it's no longer a conflict.
  28. class Action::Melee extends Action::Direction extends Action::Monster excludes name { …

    } Conflict Resolution ଟॏܧঝͰ͜Μͳ͜ͱͰ͖·͔͢ʁ Friday, September 11, 2009 Try that with multiple inheritance. Yeah right!
  29. "Roles are the sound of diamond inheritance that people have

    stopped banging their heads against." - hdp ϩʔϧͷ৔߹μΠϠϞϯυܧঝ໰୊͸ى͜Γ·ͤΜ Friday, September 11, 2009 Because of the flattening property of roles, and because of this conflict detection and resolution, the diamond inheritance problem doesn't apply to roles. Any ambiguity is a compile-time error, and the programmer has several tools to resolve such ambiguities. Role composition is much more pleasant to work with than multiple inheritance.
  30. Required Methods role Action::Role::Monster { method monster { $self->tile->monster }

    method has_monster { $self->tile->has_monster } } ಛఆͷϝιουΛඞཁͱ͢ΔϩʔϧΛ ߟ͑ͯΈ·͠ΐ͏ Friday, September 11, 2009 Suppose we have a role that assumes a particular method exists in each of its consumers. In this case, we're calling "tile" even though we don't know for sure that each consumer will have that method.
  31. Required Methods class Action::Chat with Action::Role::Monster { method run {

    print "woof" } } ͜ͷίʔυࣗମʹ໰୊͸͋Γ·ͤΜ Friday, September 11, 2009 This code will work fine. We consume the role, Action::Chat gets the new methods, and everything is hunky-dory.
  32. Required Methods Can't locate object method "tile" via package "Action::Chat"

    tileͱ͍͏ϝιου͕ඞཁͳΒίʔυͰ໌ݴ͢΂͖Ͱ͢ Friday, September 11, 2009 This sucks. The author of the Monster role demands that consumers have a "tile" method. Perhaps the role's documentation does notify you that consumers must have it, but who even reads documentation any more? It would be nice to be able to codify this requirement.
  33. Required Methods role Action::Role::Monster { requires 'tile'; method monster {

    $self->tile->monster } method has_monster { $self->tile->has_monster } } ࣗ෼Ͱ༻ҙ͍ͯ͠ͳ͍ϝιου͸ ૬खʹ༻ҙͤ͞·͠ΐ͏ Friday, September 11, 2009 And, of course, you can. If a role calls methods that it doesn't provide itself, the role should require them.
  34. Required Methods 'Action::Role::Monster' requires the method 'tile' to be implemented

    by 'Action::Chat' ͦ͏͢Ε͹ίϯύΠϧ࣌ʹΤϥʔΛग़ͤ·͢ Friday, September 11, 2009 And like a conflict, we get this error at compile time. Much better than a method-missing error at runtime when we try to call ->tile. The Moose team really likes the fail-fast principle.
  35. role Scan { requires 'entries'; method ages { map {

    $_->age } $self->entries } } Default Implementation ඞਢϝιουΛ࢖ͬͯผͷϝιουΛ࡞ΔྫͰ͢ Friday, September 11, 2009 Here we have a role that requires an "entries" method, then builds on top of it with another method "ages".
  36. class Backend::Hash with Scan { method entries { values %{

    $self->storage } } } Default Implementation ΤϯτϦΛϋογϡʹೖΕΔόοΫΤϯυΛ ༻ҙͯ͠Έ·͢ Friday, September 11, 2009 We have an example backend that stores its entries in a hash table. We provide the "entries" method that the Scan role requires. That role gives us an "ages" method that calls "entries". Pretty straightforward use of roles.
  37. class Backend::DBI with Scan { method entries { $self->q("SELECT *

    …"); } method ages { $self->q("SELECT age …"); } } Default Implementation ϝιουΛఆٛͯ͠͠·͏ྫ͸ িಥճආҎ֎Ͱ΋༗ޮͰ͢ Friday, September 11, 2009 When I was describing conflict resolution I mentioned how a method defined by the class wins over a method pulled in from a role. That's useful even outside of conflict resolution.
  38. class Backend::DBI with Scan { method entries { $self->q("SELECT *

    …"); } method ages { $self->q("SELECT age …"); } } Default Implementation σϑΥϧτͷϝιου͸࣮૷ྫͷ໾ׂ΋͠·͢ Friday, September 11, 2009 The ages method we define is doing a lot less work than the default implementation provided by the role would do. We don't need to pull in every field of every entry then pick out the values for age. However, the role's implementation is a good default that would be useful for a lot of these backends. Method overrides permit reuse but allow optimizations or alternate implementations where needed.
  39. class Backend::DBI with Scan { method entries { $self->q("SELECT *

    …"); } method ages { $self->q("SELECT age …"); } } Default Implementation ͜ͷ৔߹ͳͥScanΛ߹੒͢ΔͷͰ͠ΐ͏͔ Friday, September 11, 2009 You might be asking why we bother to consume Scan even though we don't actually pull in any methods or attributes from it. That's next!
  40. "The more I use roles, the less I understand why

    anyone would want to use the inheritance model." - Ovid ϩʔϧΛ࢖͏ͱܧঝϞσϧΛ ࢖͍͕ͨΔਓͷؾ͕஌Εͳ͘ͳΔ Friday, September 11, 2009 Roles have so many excellent features that I am starting to agree with this (admittedly radical) viewpoint myself.
  41. Role = Type Allomorphism ϩʔϧ͸ܕγεςϜͷҰ෦ʹͳΓ͑·͢ Friday, September 11, 2009 Allomorphism

    is a fancy word that means a few things. For one, roles can be part of the type system. It's a lot like duck typing, but more explicit.
  42. Role = Semantics Allomorphism ϩʔϧ͸ηϚϯςΟΫε΋҉ࣔ͠·͢ Friday, September 11, 2009 Allomorphism

    also means that a role implies semantics. Basically, every method implemented or required by the role must implement some specified behavior.
  43. Role = Semantics Allomorphism Role = Type ҙຯ͢Δͱ͜Ζ͸ಉ͡Ͱ͔͢Β·ͱΊͯઆ໌͠·͢ Friday, September

    11, 2009 These imply similar consequences, so I'm going to explain them together.
  44. role Interface::Nonblocking { requires 'read', 'write'; } Allomorphism ϩʔϧ໊͔Βread΍writeΛ Ͳ͏ॻ͚͹Α͍͔Θ͔Γ·͢

    Friday, September 11, 2009 Here we have a role that requires "read" and "write" methods from each of its consumers. Given the name of the role, we can guess that the role requires these methods to be nonblocking.
  45. class Nonblocking::Socket with Interface::Nonblocking { method read { … }

    method write { … } } Allomorphism Ϋϥεఆٛ࣌ʹ͸ϊϯϒϩοΩϯάͷཁ݅Λ ຬͨ͢Α͏ʹ͠·͢ Friday, September 11, 2009 Here we define a class that does the nonblocking interface. Nonblocking::Socket promises that its read and write methods fulfill the socket's nonblocking requirements.
  46. class Blocking::Socket { method read { … } method write

    { … } } Allomorphism ͪ͜Β͸ϒϩοΫ͢ΔΫϥεͷྫͰ͢ Friday, September 11, 2009 Here's another class, one whose read and write methods do block. Even though it fulfills the method name requirements of Interface::Nonblocking, this class would be lying if it declared that it does the role.
  47. my $s = shift; $s->does('Interface::Nonblocking') or confess 'No blocking!'; $s->write('͓͸Α͏ʂ');

    Allomorphism ͜͜Ͱ͸ϩʔϧΛຬ͔ͨ֬͢ೝ͍ͯ͠·͢ Friday, September 11, 2009 We can ask an object if it does a particular role. This will die if we try to use a Blocking::Socket here. This is better than checking "isa" because we actually care about capability. We don't care what $s's class is, or what its ancestors are. Any class can declare that it does the Interface::Nonblocking role, as long as it fulfills the role's contract.
  48. my $s = shift; $s->can('read') && $s->can('write') or confess 'Need

    an interface'; $s->write('͓͸Α͏ʂ'); Duck Typing μοΫλΠϐϯάͩͱηϚϯςΟΫε͕Θ͔Γ·ͤΜ Friday, September 11, 2009 Duck typing fulfills this same need. We also don't care about what class $s is, as long as it has the methods we want. However, the problem with duck typing is that merely having a set of methods does not imply semantics. Perhaps read and write are actually going to do text-to-speech and printing a term paper. Or worse, they might block.
  49. my $s = shift; $s->does('Interface::Nonblocking') or confess 'No blocking!'; $s->write('͓͸Α͏ʂ');

    Allomorphism write͕ϒϩοΫ͠ͳ͍ͱΘ͔Δͷ͸ ϩʔϧͷ͓͔͛Ͱ͢ Friday, September 11, 2009 In any case, because the Interface::Nonblocking role requires the write method, we know that $s will not only have it, but we know it will not block.
  50. has connection => ( isa => 'Nonblocking::Socket', ); Allomorphism ΞτϦϏϡʔτͷ৔߹΋΄ͱΜͲಉ͡Ͱ͕͢

    Friday, September 11, 2009 Moose has support for allomorphism in attributes. Instead of demanding that connection be a particular class...
  51. has connection => ( isa => 'Nonblocking::Socket', does => 'Interface::Nonblocking',

    ); Allomorphism ಛఆͷϩʔϧΛຬͨ͢ίωΫγϣϯͷ ஋ΛཁٻͰ͖·͢ Friday, September 11, 2009 … we can demand that the value of connection does a particular role.
  52. "A role-aware type system allows you to express yourself with

    better genericity." - chromatic ΠϯλϑΣʔε͑͞ຬͨͤ͹Α͍ͱ͍͏ͷ͕ϙ ΠϯτͰ͢ Friday, September 11, 2009 chromatic's point here is that you can stop caring about hierarchy and start caring about capabilities. Allomorphism means you don't need to subclass someone's crack-fueled module. You need only fulfill its crack-fueled interface. It's OOP freedom.
  53. class WWW::Mechanize::TreeBuilder extends WWW::Mechanize { has tree => ( …

    ); around _make_request { … } } Extensions ϞδϡʔϧΛ֦ு͢Δͱ͖͸;ͭ͏ αϒΫϥεʹ͠·͢ΑͶʁ Friday, September 11, 2009 Here's a quick tip for you. Say you're extending a module. Ordinarily you'd write a subclass, right?
  54. class Test::WWW::Mech::TreeBuilder extends Test::WWW::Mechanize { has tree => ( …

    ); around _make_request { … } } Extensions ςετͷํ΋αϒΫϥεʹ͠·͢ Friday, September 11, 2009 You write good tests, so you subclass its test subclass too.
  55. class WWW::Mech::Cache::TreeBuilder extends WWW::Mechanize::Cached { has tree => ( …

    ); around _make_request { … } } Extensions ΄͔ͷαϒΫϥε΋ʁ Mechʹύον౰ͯͨํ͕Α͘ͳ͍ʁ Friday, September 11, 2009 You also have to subclass all the other subclasses you use. Maybe it'd be better to just monkeypatch WWW::Mechanize. Who'd know?
  56. BAD! Extensions ͦ͏͍͏͜ͱ͸ɺͪ͠Ό͍͚·ͤΜʂ Friday, September 11, 2009 No! Don't do

    it! You'll screw it up for anyone else who happens to use that module in your codebase.
  57. role WWW::Mechanize::TreeBuilder { has tree => ( … ); around

    _make_request { … } } Extensions ֦ுΛϩʔϧʹͯ͠͠·͍·͠ΐ͏ Friday, September 11, 2009 Just make your extension a role.
  58. class Test::WWW::Mech::TreeBuilder extends Test::WWW::Mechanize with WWW::Mechanize::TreeBuilder {} Extensions ͋ͱ͸ϩʔϧΛద༻͢Δ͚ͩɻলུͳΜͯ͋Γ·ͤΜ Friday,

    September 11, 2009 Now you can apply this role to the existing subclasses of the module. There's no dot-dot-dot in the braces here. This is a full class definition.
  59. class WWW::Mech::Cache::TreeBuilder extends WWW::Mechanize::Cached with WWW::Mechanize::TreeBuilder {} Extensions ֦ு෦෼͸͢΂ͯϩʔϧͷதͰ͢ɻ͖Ε͍Ͱ͠ΐʁ Friday,

    September 11, 2009 That's really it. You don't need any other code. All the extension code is in your role. Nice and clean.
  60. Roles > Inheritance Extensions ͳΔ΂͘ܧঝ͠ͳ͍Ͱϩʔϧʹ͢Δ͜ͱ Friday, September 11, 2009 There,

    I said it! If you can implement your extension as a role, do it. Use inheritance sparingly.
  61. role Rootey { method su { … } } $jrock

    = User->new("jrockway"); $shawn = User->new("sartak"); Rootey->meta->apply($shawn); $shawn->su; # ok $jrock->su; # dies! Role -> Object ϩʔϧΛಛఆͷΦϒδΣΫτʹ ద༻͢Δ͜ͱ΋Ͱ͖·͢ Friday, September 11, 2009 I don't want to spend much time on this, but you can apply a role to a particular object. It doesn't have to be a full-blown class. It also won't affect any other objects. Which is good, because I wouldn't trust Jon with root.
  62. Plugins MooseX::Object::Pluggable MooseX::Traits ࡢ೔࿩ͨ͠Α͏ʹɺϩʔϧ͸ϓϥάΠϯʹ΋࢖͑·͢ Friday, September 11, 2009 Roles are

    nice for plugins too. I covered this heavily in my API Design talk. The idea is each plugin is just a role. These two modules make roles-as-plugins very easy. http://sartak.org/talks/yapc-asia-2009/api-design/
  63. Classes are Nouns Message User Service Request Response Ϋϥεͱ͍͏ͷ͸͍ΘΏΔ໊ࢺͰ͢ Friday,

    September 11, 2009 To give you an idea of how to think about roles, here's a pretty simple metaphor that works for me. Classes are nouns.
  64. Methods are Verbs send_message add_user connect_to_service handle_request serve_response ϝιου͸ಈࢺͰ͢Ͷ Friday,

    September 11, 2009 Methods are verbs. They are simple behaviors. They do things.
  65. Roles are Adjectives HasPlugins RequiresPlugins Throwable Reblessing ExtendsObject::User ϩʔϧ͸ܗ༰ࢺͰ͢ Friday,

    September 11, 2009 Roles are adjectives. These are all good role names. If you think of some piece of behavior as an adjective, that's a good sign that it can be factored out as a role.
  66. role Scan { requires 'entries'; method ages { map {

    $_->age } $self->entries } } Parameterized Roles ͜Ε͸·͞ʹύϥϝʔλ͕ඞཁͳϩʔϧͷྫͰ͢ Friday, September 11, 2009 This is a very specific kind of parameterized role. Each consumer parameterizes the "ages" method by providing an "entries" method. But that's not really what I'm talking about when I say parameterized role. If you can do this, do it. If you need something more advanced...
  67. Parameterized Roles role Counter (Int :$default = 0) { has

    counter => ( is => 'ro', isa => 'Int', default => $default, ); sub increment { my $self = shift; $self->counter($self->counter + 1); } } ͜ΕΛ࢖͏ͱ૊ΈࠐΉଆ͕σϑΥϧτ஋Λ એݴͰ͖·͢ Friday, September 11, 2009 Here we have our old Counter role, but now each consumer can declare what default it wants for the attribute. If they choose nothing, they get the default of 0. rafl++ added parameterized role support to MooseX::Declare recently, so my examples get to look much nicer.
  68. Parameterized Roles 66 class Odometer with Counter(default => 10000) {

    … } Ҿ਺Λ౉͍ͯ͠Δ͜ͱҎ֎ɺ࢖͍ํ͸΄΅ಉ͡Ͱ͢ Friday, September 11, 2009 Using a parameterized role is pretty much the same. You just pass in the named arguments to "with". This was an example of parameterizing an attribute, and is probably the most common use of p-roles.
  69. Parameterized Roles 66 role Speedometer (Str :$method) { around $method

    { my $start = time; $orig->(@_); print "$method " . (time - $start); } } ͜Ε͸ಛఆͷϝιουʹϓϩϑΝΠϥΛ ௥Ճ͢ΔϩʔϧͰ͢ Friday, September 11, 2009 Here we have a role that wraps any method you give it with a bit of profiling code. This is sort of the inverse of "requires". Instead of the role telling you what method you need, you tell the role what method it needs to instrument. Someone once praised this type of p-role usage as resembling macros.
  70. Parameterized Roles 66 role WWW::Search (Str :$engine) { method search

    (Str $query) { if ($engine eq "Google") { REST::Google::Search->… } elsif ($engine eq "Yahoo!") { Yahoo::Search->… } elsif ($engine eq "Bing") { die "seriously?"; } # post-process @results } } ύϥϝʔλ෇͖ϩʔϧͷ௕ॴ͸ίʔυͷ ࠶ར༻ੑ͕ߴ·Δ͜ͱ Friday, September 11, 2009 One really nice thing about parameterized roles is that they improve code reuse. All the consumer has to do is inform the role of which search engine they want to use. The role takes care of the rest. This means each consumer writes less code, because the parameterized role can build up more structure.
  71. Parameterized Roles 66 package Valid::Adding; use MooseX::Role::Parameterized; role { $consumer->does('Adding')

    or die; $consumer->add(2, 6) == 8 or die; method safe_add => sub { … }; }; ͜ͷΑ͏ʹ௥ՃͷόϦσʔγϣϯʹ΋ར༻Ͱ͖·͢ Friday, September 11, 2009 Here's another use case that was raised recently. The parameterized role could perform additional validation on each consumer. This is like "requires" for method names, but stronger. This is what pre-MooseX::Declare parameterized roles look like, due to language constraints.
  72. Roles Class building blocks Conflicts, resolution Required methods Allomorphism (types)

    Extensions, Plugins "adjectives" Έͳ͞Μ΋ͥͻϩʔϧΛ࢖ͬͯΈ͍ͯͩ͘͞ Friday, September 11, 2009 Roles are all of these things. I hope I've convinced you to use roles in your next project's design. Thank you!
  73. Thanks to my reviewers Vlad Dogsby Thomas Figg Alexander Hoffer

    Stevan Little Lucas Oman Simon Pollard Friday, September 11, 2009 Thanks to these people who have reviewed my slides and offered excellent advice.
  74. Thanks to my translator Kenichi Ishigaki charsbar++ Friday, September 11,

    2009 Thank you to Ishigaki-san for translating my slides!
  75. See Also Moose::Manual::Roles "The Why of Perl Roles" "Eliminating Inheritance

    via Smalltalk-Style Traits" "Traits - Composing Classes from Behavioral Building Blocks" Friday, September 11, 2009 http://search.cpan.org/perldoc?Moose::Manual::Roles http://www.modernperlbooks.com/mt/2009/04/the-why-of-perl-roles.html http://use.perl.org/~Ovid/journal/39404 http://scg.unibe.ch/archive/phd/schaerli-phd.pdf