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

Announcing Announcements

Announcing Announcements

Say you're writing a system that uses the observer design pattern. What happens if instead of passing around the NAME of an event, you pass around an OBJECT representing the event? Turns out, a whole lot of goodness!

Announcements enable communication from the observer to the observed, and even communication (and conspiracy) amongst observers themselves.

All with a very strongly object-oriented, antler-shaped flavor.

Shawn Moore

October 23, 2011
Tweet

More Decks by Shawn Moore

Other Decks in Programming

Transcript

  1. package Pattern::Ann!ncements; use vars qʮ@ISAʯ; @ISA = qqʲPattern::Observerʳ; 4 11೥6݄28೔Ր༵೔

    Announcements are a lot like the Observer pattern except a bit more specialized.
  2. 5 11೥6݄28೔Ր༵೔ Let’s say we’re modeling a trial. We want

    an object for the defendant, and an object for each of the jurors. When the defendant pleas innocent or guilty, we have to tell the jurors about it. We don’t want to just hardcode a list of jurors, or even store the jurors in an attribute on the defendant object, because that’s tight coupling. After all, the jurors are supposed to have no relationship with the defendant. Instead, we can have the jurors subscribe to the defendant’s plea event, which leads to a nice abstraction barrier.
  3. Innocent! 5 11೥6݄28೔Ր༵೔ Let’s say we’re modeling a trial. We

    want an object for the defendant, and an object for each of the jurors. When the defendant pleas innocent or guilty, we have to tell the jurors about it. We don’t want to just hardcode a list of jurors, or even store the jurors in an attribute on the defendant object, because that’s tight coupling. After all, the jurors are supposed to have no relationship with the defendant. Instead, we can have the jurors subscribe to the defendant’s plea event, which leads to a nice abstraction barrier.
  4. Innocent! 5 11೥6݄28೔Ր༵೔ Let’s say we’re modeling a trial. We

    want an object for the defendant, and an object for each of the jurors. When the defendant pleas innocent or guilty, we have to tell the jurors about it. We don’t want to just hardcode a list of jurors, or even store the jurors in an attribute on the defendant object, because that’s tight coupling. After all, the jurors are supposed to have no relationship with the defendant. Instead, we can have the jurors subscribe to the defendant’s plea event, which leads to a nice abstraction barrier.
  5. Innocent! 5 11೥6݄28೔Ր༵೔ Let’s say we’re modeling a trial. We

    want an object for the defendant, and an object for each of the jurors. When the defendant pleas innocent or guilty, we have to tell the jurors about it. We don’t want to just hardcode a list of jurors, or even store the jurors in an attribute on the defendant object, because that’s tight coupling. After all, the jurors are supposed to have no relationship with the defendant. Instead, we can have the jurors subscribe to the defendant’s plea event, which leads to a nice abstraction barrier.
  6. Innocent! 5 11೥6݄28೔Ր༵೔ Let’s say we’re modeling a trial. We

    want an object for the defendant, and an object for each of the jurors. When the defendant pleas innocent or guilty, we have to tell the jurors about it. We don’t want to just hardcode a list of jurors, or even store the jurors in an attribute on the defendant object, because that’s tight coupling. After all, the jurors are supposed to have no relationship with the defendant. Instead, we can have the jurors subscribe to the defendant’s plea event, which leads to a nice abstraction barrier.
  7. Innocent! 5 11೥6݄28೔Ր༵೔ Let’s say we’re modeling a trial. We

    want an object for the defendant, and an object for each of the jurors. When the defendant pleas innocent or guilty, we have to tell the jurors about it. We don’t want to just hardcode a list of jurors, or even store the jurors in an attribute on the defendant object, because that’s tight coupling. After all, the jurors are supposed to have no relationship with the defendant. Instead, we can have the jurors subscribe to the defendant’s plea event, which leads to a nice abstraction barrier.
  8. Innocent! 5 11೥6݄28೔Ր༵೔ Let’s say we’re modeling a trial. We

    want an object for the defendant, and an object for each of the jurors. When the defendant pleas innocent or guilty, we have to tell the jurors about it. We don’t want to just hardcode a list of jurors, or even store the jurors in an attribute on the defendant object, because that’s tight coupling. After all, the jurors are supposed to have no relationship with the defendant. Instead, we can have the jurors subscribe to the defendant’s plea event, which leads to a nice abstraction barrier.
  9. Innocent! 5 11೥6݄28೔Ր༵೔ Let’s say we’re modeling a trial. We

    want an object for the defendant, and an object for each of the jurors. When the defendant pleas innocent or guilty, we have to tell the jurors about it. We don’t want to just hardcode a list of jurors, or even store the jurors in an attribute on the defendant object, because that’s tight coupling. After all, the jurors are supposed to have no relationship with the defendant. Instead, we can have the jurors subscribe to the defendant’s plea event, which leads to a nice abstraction barrier.
  10. Innocent! 5 11೥6݄28೔Ր༵೔ Let’s say we’re modeling a trial. We

    want an object for the defendant, and an object for each of the jurors. When the defendant pleas innocent or guilty, we have to tell the jurors about it. We don’t want to just hardcode a list of jurors, or even store the jurors in an attribute on the defendant object, because that’s tight coupling. After all, the jurors are supposed to have no relationship with the defendant. Instead, we can have the jurors subscribe to the defendant’s plea event, which leads to a nice abstraction barrier.
  11. Observer Pattern Publisher Subscriber Subscriber Subscriber 9 11೥6݄28೔Ր༵೔ In the

    observer pattern, when an event happens, the publisher tells each of the subscribers about it. The publisher might call a method on each of the subscriber objects passing in the event name as a string. The salient point about Observer is that it doesn’t hardcode the list of subscribers; it’s dynamic.
  12. Observer Pattern Publisher Subscriber Subscriber Subscriber event 9 11೥6݄28೔Ր༵೔ In

    the observer pattern, when an event happens, the publisher tells each of the subscribers about it. The publisher might call a method on each of the subscriber objects passing in the event name as a string. The salient point about Observer is that it doesn’t hardcode the list of subscribers; it’s dynamic.
  13. Observer Pattern Publisher Subscriber Subscriber Subscriber 9 11೥6݄28೔Ր༵೔ In the

    observer pattern, when an event happens, the publisher tells each of the subscribers about it. The publisher might call a method on each of the subscriber objects passing in the event name as a string. The salient point about Observer is that it doesn’t hardcode the list of subscribers; it’s dynamic.
  14. Announcer Pattern Announcer Subscriber Subscriber Subscriber 10 11೥6݄28೔Ր༵೔ Instead, with

    announcements, when an event worth noting occurs, the announcer creates an announcement OBJECT. Then passes it to each of the subscribers. But the pattern looks more like this, because the same object is passed to each of the subscribers. And then when it’s done being handed out, the Announcer still has that object in the end. And when the announcer is done with it, it’s garbage collected. Since we’re done with this slide, it’s garbage collected. Oh, thought I missed one? No ma’am.
  15. Announcer Pattern Announcer Subscriber Subscriber Subscriber announcement!! 10 11೥6݄28೔Ր༵೔ Instead,

    with announcements, when an event worth noting occurs, the announcer creates an announcement OBJECT. Then passes it to each of the subscribers. But the pattern looks more like this, because the same object is passed to each of the subscribers. And then when it’s done being handed out, the Announcer still has that object in the end. And when the announcer is done with it, it’s garbage collected. Since we’re done with this slide, it’s garbage collected. Oh, thought I missed one? No ma’am.
  16. Announcer Pattern Announcer Subscriber Subscriber Subscriber announcement!! 10 11೥6݄28೔Ր༵೔ Instead,

    with announcements, when an event worth noting occurs, the announcer creates an announcement OBJECT. Then passes it to each of the subscribers. But the pattern looks more like this, because the same object is passed to each of the subscribers. And then when it’s done being handed out, the Announcer still has that object in the end. And when the announcer is done with it, it’s garbage collected. Since we’re done with this slide, it’s garbage collected. Oh, thought I missed one? No ma’am.
  17. Announcer Pattern Announcer Subscriber Subscriber Subscriber announcement!! 10 11೥6݄28೔Ր༵೔ Instead,

    with announcements, when an event worth noting occurs, the announcer creates an announcement OBJECT. Then passes it to each of the subscribers. But the pattern looks more like this, because the same object is passed to each of the subscribers. And then when it’s done being handed out, the Announcer still has that object in the end. And when the announcer is done with it, it’s garbage collected. Since we’re done with this slide, it’s garbage collected. Oh, thought I missed one? No ma’am.
  18. Announcer Pattern Announcer Subscriber Subscriber Subscriber announcement!! 10 11೥6݄28೔Ր༵೔ Instead,

    with announcements, when an event worth noting occurs, the announcer creates an announcement OBJECT. Then passes it to each of the subscribers. But the pattern looks more like this, because the same object is passed to each of the subscribers. And then when it’s done being handed out, the Announcer still has that object in the end. And when the announcer is done with it, it’s garbage collected. Since we’re done with this slide, it’s garbage collected. Oh, thought I missed one? No ma’am.
  19. Announcer Pattern Announcer Subscriber Subscriber Subscriber announcement!! 10 11೥6݄28೔Ր༵೔ Instead,

    with announcements, when an event worth noting occurs, the announcer creates an announcement OBJECT. Then passes it to each of the subscribers. But the pattern looks more like this, because the same object is passed to each of the subscribers. And then when it’s done being handed out, the Announcer still has that object in the end. And when the announcer is done with it, it’s garbage collected. Since we’re done with this slide, it’s garbage collected. Oh, thought I missed one? No ma’am.
  20. Announcer Pattern Announcer Subscriber Subscriber Subscriber 10 11೥6݄28೔Ր༵೔ Instead, with

    announcements, when an event worth noting occurs, the announcer creates an announcement OBJECT. Then passes it to each of the subscribers. But the pattern looks more like this, because the same object is passed to each of the subscribers. And then when it’s done being handed out, the Announcer still has that object in the end. And when the announcer is done with it, it’s garbage collected. Since we’re done with this slide, it’s garbage collected. Oh, thought I missed one? No ma’am.
  21. Announcer Pattern 10 11೥6݄28೔Ր༵೔ Instead, with announcements, when an event

    worth noting occurs, the announcer creates an announcement OBJECT. Then passes it to each of the subscribers. But the pattern looks more like this, because the same object is passed to each of the subscribers. And then when it’s done being handed out, the Announcer still has that object in the end. And when the announcer is done with it, it’s garbage collected. Since we’re done with this slide, it’s garbage collected. Oh, thought I missed one? No ma’am.
  22. Announcer Pattern 10 11೥6݄28೔Ր༵೔ Instead, with announcements, when an event

    worth noting occurs, the announcer creates an announcement OBJECT. Then passes it to each of the subscribers. But the pattern looks more like this, because the same object is passed to each of the subscribers. And then when it’s done being handed out, the Announcer still has that object in the end. And when the announcer is done with it, it’s garbage collected. Since we’re done with this slide, it’s garbage collected. Oh, thought I missed one? No ma’am.
  23. Announcements 11 11೥6݄28೔Ր༵೔ Announcements are not a new idea; they

    are constantly reinvented. They’re not particularly brilliant either, they’re a pretty straightforward extension of a common pattern. Furthermore, they’re so not-brilliant that hopefully they stick in your mind so that in 1, or 8 years you’ll remember this talk and have no trouble busting these bad boys out. Even if you don’t actually remember the name “Announcements” you’ll be alright.
  24. Announcements • Not new 11 11೥6݄28೔Ր༵೔ Announcements are not a

    new idea; they are constantly reinvented. They’re not particularly brilliant either, they’re a pretty straightforward extension of a common pattern. Furthermore, they’re so not-brilliant that hopefully they stick in your mind so that in 1, or 8 years you’ll remember this talk and have no trouble busting these bad boys out. Even if you don’t actually remember the name “Announcements” you’ll be alright.
  25. Announcements • Not new • Not brilliant 11 11೥6݄28೔Ր༵೔ Announcements

    are not a new idea; they are constantly reinvented. They’re not particularly brilliant either, they’re a pretty straightforward extension of a common pattern. Furthermore, they’re so not-brilliant that hopefully they stick in your mind so that in 1, or 8 years you’ll remember this talk and have no trouble busting these bad boys out. Even if you don’t actually remember the name “Announcements” you’ll be alright.
  26. Announcements • Not new • Not brilliant • Obvious 11

    11೥6݄28೔Ր༵೔ Announcements are not a new idea; they are constantly reinvented. They’re not particularly brilliant either, they’re a pretty straightforward extension of a common pattern. Furthermore, they’re so not-brilliant that hopefully they stick in your mind so that in 1, or 8 years you’ll remember this talk and have no trouble busting these bad boys out. Even if you don’t actually remember the name “Announcements” you’ll be alright.
  27. Announcements • Not new • Not brilliant • Obvious •

    Life-changing 11 11೥6݄28೔Ր༵೔ Announcements are not a new idea; they are constantly reinvented. They’re not particularly brilliant either, they’re a pretty straightforward extension of a common pattern. Furthermore, they’re so not-brilliant that hopefully they stick in your mind so that in 1, or 8 years you’ll remember this talk and have no trouble busting these bad boys out. Even if you don’t actually remember the name “Announcements” you’ll be alright.
  28. Announcements 12 11೥6݄28೔Ր༵೔ An announcement is a notification about an

    event. Each announcement is an object. And of course, objects can have attributes. And methods. Which means announcements can have attributes and methods. I’ll let that sink in for a moment.
  29. Announcements • Event notification 12 11೥6݄28೔Ր༵೔ An announcement is a

    notification about an event. Each announcement is an object. And of course, objects can have attributes. And methods. Which means announcements can have attributes and methods. I’ll let that sink in for a moment.
  30. Announcements • Event notification • An object 12 11೥6݄28೔Ր༵೔ An

    announcement is a notification about an event. Each announcement is an object. And of course, objects can have attributes. And methods. Which means announcements can have attributes and methods. I’ll let that sink in for a moment.
  31. Announcements • Event notification • An object • with attributes

    12 11೥6݄28೔Ր༵೔ An announcement is a notification about an event. Each announcement is an object. And of course, objects can have attributes. And methods. Which means announcements can have attributes and methods. I’ll let that sink in for a moment.
  32. Announcements • Event notification • An object • with attributes

    • and methods 12 11೥6݄28೔Ր༵೔ An announcement is a notification about an event. Each announcement is an object. And of course, objects can have attributes. And methods. Which means announcements can have attributes and methods. I’ll let that sink in for a moment.
  33. Announcing with ‘Announcements::Announcing’; 13 11೥6݄28೔Ր༵೔ If you want to announce

    something you consume the Announcing role. That’s the only real Moosiness in this framework. When you consume this role you get these two methods.
  34. Announcing with ‘Announcements::Announcing’; • announce 13 11೥6݄28೔Ր༵೔ If you want

    to announce something you consume the Announcing role. That’s the only real Moosiness in this framework. When you consume this role you get these two methods.
  35. Announcing with ‘Announcements::Announcing’; • announce • add_subscription 13 11೥6݄28೔Ր༵೔ If

    you want to announce something you consume the Announcing role. That’s the only real Moosiness in this framework. When you consume this role you get these two methods.
  36. $defendant->add_subscription( when => ‘Plead’, do => sub { my $plead

    = shift; $juror->deliberate($plead); }, ); 14 11೥6݄28೔Ր༵೔ Here’s what it looks like to subscribe to an announcement.
  37. $defendant->add_subscription( when => ‘Plead’, do => sub { my $plead

    = shift; $juror->deliberate($plead); }, ); Announcement class name 15 11೥6݄28೔Ր༵೔ Since an announcement is an object, part of subscribing means you choose a class name to filter only the announcements you care about.
  38. $defendant->add_subscription( when => ‘Plead’, do => sub { my $plead

    = shift; $juror->deliberate($plead); }, ); Announcer 16 11೥6݄28೔Ր༵೔ You subscribe to announcements from a particular object.
  39. $defendant->add_subscription( when => ‘Plead’, do => sub { my $plead

    = shift; $juror->deliberate($plead); }, ); Announcement object 17 11೥6݄28೔Ր༵೔ In the reaction code, you get the announcement object.
  40. Announcing $announcement = Plead::Innocent->new; $defendant->announce($announcement); 18 11೥6݄28೔Ր༵೔ To actually announce

    something you create the announcement object then call the announce method passing it. Then after the announcement object has been passed around to all of the subscribers, the announcer gets it back in the end. Which means you can examine attributes that subscribers set, for example.
  41. Announcing $announcement = Plead::Innocent->new; $defendant->announce($announcement); if ($announcement->is_convicted) { die; }

    18 11೥6݄28೔Ր༵೔ To actually announce something you create the announcement object then call the announce method passing it. Then after the announcement object has been passed around to all of the subscribers, the announcer gets it back in the end. Which means you can examine attributes that subscribers set, for example.
  42. Observer Pattern Publisher Subscriber Subscriber Subscriber 19 11೥6݄28೔Ր༵೔ In the

    observer pattern you don’t GET two way communication. In announcements you do.
  43. communicate across the information superhighway object network 20 11೥6݄28೔Ր༵೔ That’s

    why Announcements are “communicate across the object network” and not “a framework for event notification”
  44. Communication • veto • vote • select • three-phase commit

    21 11೥6݄28೔Ր༵೔ This communication which is the heart of why announcements are cool enables some interesting new patterns. I’ll show a quick example of each of these.
  45. package Company; sub set_president { my $self = shift; my

    $new_prez = shift; $self->{president} = $new_prez; } 22 11೥6݄28೔Ր༵೔ A company class might have a president attribute. This is a straightforward writer accessor. Let’s jazz it up with announcements.
  46. sub set_president { my $self = shift; my $new_prez =

    shift; my $change = ChangePresident->new( old => $self->president, new => $new_prez, ); $self->announce($change); $self->{president} = $new_prez; } 23 11೥6݄28೔Ր༵೔ We can announce when we’re about to change the president.
  47. sub set_president { my $self = shift; my $new_prez =

    shift; my $change = ChangePresident->new( old => $self->president, new => $new_prez, ); $self->announce($change); return if $change->is_vetoed; $self->{president} = $new_prez; } 24 11೥6݄28೔Ր༵೔ Here’s another cool thing you can do. Allow ChangePresident to be vetoed!
  48. package ChangePresident; use Moose; extends ‘Announcements::Announcement’; has [‘old_value’, ‘new_value’] =>

    ( is => ‘ro’, ); has is_vetoed => ( is => ‘rw’, isa => ‘Bool’, ); sub veto { shift->is_vetoed(1) } 25 11೥6݄28೔Ր༵೔ Just add an is_vetoed attribute.
  49. $bluth_company->add_subscription( when => ‘ChangePresident’, do => sub { my $change

    = shift; if ($change->new_value eq ‘Jobe’) { $change->is_veto; } }, ); 26 11೥6݄28೔Ր༵೔ Now that we have the is_vetoed attribute on the ChangePresident announcement, we can set it in a subscriber and it’ll filter back up to the announcer, thus blocking the change.
  50. package ChangePresident; use Moose; extends ‘Announcements::Announcement’; has [‘old_value’, ‘new_value’] =>

    ( is => ‘ro’, ); has is_vetoed => ( is => ‘rw’, isa => ‘Bool’, ); sub veto { shift->is_vetoed(1) } 27 11೥6݄28೔Ր༵೔ This veto code could be a role for announcements that you put on CPAN as, I dunno...
  51. <a onclick=“event.preventDefault();”> 29 11೥6݄28೔Ր༵೔ This may seem crazy and wild

    but I bet most of you have seen the announcement veto pattern before.
  52. <a onclick=“event.preventDefault();”> Announcement object 31 11೥6݄28೔Ր༵೔ The really cool thing

    is that this announcement crosses language barriers. The browser, probably written in C++, constructs an announcement that can be passed into javascript code. Then the javascript code can “veto” the bubbling up that the browser’s C++ code will do.
  53. Communication • veto • vote • select • three-phase commit

    32 11೥6݄28೔Ր༵೔ This communication which is the heart of why announcements are cool enables some interesting new patterns. I’ll show a quick example of each of these.
  54. has score => ( is => ‘rw’, isa => ‘Int’,

    default => 0, ); sub vote_up { my $self = shift; $self->score($self->score + 1); } sub vote_down { my $self = shift; $self->score($self->score - 1); } 33 11೥6݄28೔Ր༵೔ You can get as complicated as you like with voting. Here we maintain a score and we can reject the change if the score is not positive.
  55. has items => ( is => ‘ro’, isa => ‘Set[Item]’,

    ); has selected => ( is => ‘ro’, isa => ‘Set[Item]’, ); sub select { $self->selected->add(@_); } 34 11೥6݄28೔Ր༵೔ It doesn’t have to be voting on one thing either. You can have a list of items and have the subscribers select which items to operate on. This is one of the ways we used Announcements in my NetHack bot - when we saw items on the ground we asked the AI components which items to pick up using code like this.
  56. Three-Phase Commit • about to change • will change •

    changed 35 11೥6݄28೔Ր༵೔ The “announcements” hello world seems to be the three-phase commit. When you want to change something, you send an announcement stating your intent. That can be vetoed. If that passes, you send another announcement saying you’re definitely about to change the value, but it hasn’t happened yet. Then you change the value and send one final announcement saying you just did that.
  57. Three-Phase Commit • about to change -- vetoable • will

    change -- not vetoable • changed -- not vetoable 36 11೥6݄28೔Ր༵೔ You might wonder why there are two before announcements. That’s because the first announcement you send out can be vetoed. If a subscriber vetoes that announcement then subscribers that need to hook into a more secure “this is definitely going to happen” announcement are being lied to. And of course, after the change already happens, it’s way too late to veto it. I’d show some code here but you already saw the interesting vetoable announcement.
  58. $announcer->subscribe( when => ‘Announcements::Announcement’, do => sub { my $announcement

    = shift; use Data::Dumper; print Dumper($announcement); }, ); Announcement Spy 37 11೥6݄28೔Ր༵೔ You can subscribe to all of the events, since they all inherit from Announcements::Announcement, generated by a particular object and dump them for debugging purposes. That’s yet another feature you typically don’t get with observer pattern libraries.
  59. Custom Subscriptions package Announcements::Subscription; sub matches { my $self =

    shift; my $announce = shift; return $announce->DOES($self->when); } 38 11೥6݄28೔Ր༵೔ The subscription class has a method called “matches” that determines whether a particular announcement matches the subscription. The default “matches” uses DOES which in Perl 5.10 just uses “isa”, but Moose extends the DOES method to know about roles. This means you can subscribe to announcements by role name instead of by class, even in the default framework.
  60. Custom Subscriptions return $announce->DOES($self->when) && $announce->value eq $self->wants; 39 11೥6݄28೔Ր༵೔

    In your subscription subclass you could have any extra logic you want in the matcher method. This enables some pretty crazy flexibility. There’s even more flexibility here because there’s a SubscriptionRegistry object, but I don’t even know how to usefully extend that one.
  61. Announcements 40 11೥6݄28೔Ր༵೔ Announcements are an idea that were formalized

    by Vassili Bykov for Cincom Smalltalk. Piers Cawley (who is here today) brought them to Ruby and put the idea in my head. And now I put the on CPAN as Announcements. Hooray for namespace squatting.
  62. Announcements • originally from Smalltalk (Vassili Bykov) 40 11೥6݄28೔Ր༵೔ Announcements

    are an idea that were formalized by Vassili Bykov for Cincom Smalltalk. Piers Cawley (who is here today) brought them to Ruby and put the idea in my head. And now I put the on CPAN as Announcements. Hooray for namespace squatting.
  63. Announcements • originally from Smalltalk (Vassili Bykov) • via Ruby

    (Piers Cawley) 40 11೥6݄28೔Ր༵೔ Announcements are an idea that were formalized by Vassili Bykov for Cincom Smalltalk. Piers Cawley (who is here today) brought them to Ruby and put the idea in my head. And now I put the on CPAN as Announcements. Hooray for namespace squatting.
  64. Announcements • originally from Smalltalk (Vassili Bykov) • via Ruby

    (Piers Cawley) • Announcements.pm on CPAN (me) 40 11೥6݄28೔Ր༵೔ Announcements are an idea that were formalized by Vassili Bykov for Cincom Smalltalk. Piers Cawley (who is here today) brought them to Ruby and put the idea in my head. And now I put the on CPAN as Announcements. Hooray for namespace squatting.