Target Audience: This series is pitched at developers interested in Object Oriented Design Patterns using the Moose framework in Perl. I cover the examples from the Head First Design Patterns book, replicating Java implementation in Moose. You need to obtain a copy of the book, which is not hard to come by.
When I was a kid, my dad would tell me that what keen observation powers are what makes a person excel in analysis. That is borne out by Arthur Conan Doyle's prodigious creation, Sherlock Holmes.
The Observer pattern thus starts with much going for it!
It is one of my favorite patterns. Chiefly because dynamic interplay of objects that send and receive messages is a very powerful paradigm for making things happen. And the Observer pattern achieves just that!
The folks at HFDP present this as a one-many relationship between a 'Subject' that upon changing state sends notification to 'Observers' that have subscribed to listen-in. Think of a many-many relationship. It is just a collection of one-many relationships. Now think of an Observer that also implements the Subject interface. (We'll come to interfaces in a minute. Just pretend it means an Observer can behave like a Subject.) Now you have Object-Oriented Utopia. Or at least, a democracy where empowered objects send, receive and cascade messages to each other, subscribing or unsubscribing at will.
At HFDP, the story starts with a Weather Station API. The Weather Station captures 3 variables: temperature, pressure and humidity. Any time the measurements change, various Display Classes that plug-in to the Weather Station API are notified so as to render the latest information on the weather. The business model is the Weather Station company charges for Display Elements that plug-in to their API.
(Sounds familiar? Think of the Facebook API.)
For the rubber hitting road, this means the Observer pattern is in business!
A draft Weather Station can be sketched as follows:
package WeatherData;
use Moose;
has 'temperature' => (
is => 'rw',
isa => 'Value',
);
has 'humidity' => (
is => 'rw',
isa => 'Num',
);
has 'pressure' => (
is => 'rw',
isa => 'Num',
);
sub measurementsChanged {
# plug-in API
}
This bit is self-explanatory.
The principle underlying the Observer pattern is that the Subject holds copies of (references to) registered Observers. The Subject's responsibility is to provide methods for registration and un-registration. The Observer's responsibility is to make available an 'update' method that the Subject can invoke on all registered Observers. Simple?
Here then, is the abstract Subject:
package Subject;
use Moose;
sub _registerObserver {};
sub removeObserver {};
sub notifyObservers {};
and the prototypical (i.e. abstract) Observer
package Observer;
use Moose;
sub update {};
Think of these prototypes as 'roles' - little packets of functionality which allow re-use of code. Classes can be composed with them - or in Moose parlance, Classes consume roles. Do you see how this differs from conventional inheritance? The leitmotif is flexibility in terms of code re-use, regardless of the specifics of implementation. As you shall see, I do indeed use multiple inheritance to achieve composition.
That is because Perl (and Moose) support multiple inheritance. Java doesn't, and there composition with roles (the Java term is 'interfaces') needs to follow a stricter syntactic demarcation from inheritance.
The last prototype (or role, or interface, or abstract class) is:
package DisplayElement;
use Moose;
sub display {};
You got to love how easy that was!
Let's compose the Subject role into the Weather Station. Note the use of the Method Modifier 'after' to hook into the methods of the abstract implementation.
package WeatherData;
use Moose;
extends 'Subject';
has 'observers' => (
is => 'ro',
isa => 'ArrayRef[Observer]',
default => sub {[]},
predicate => 'has_observers',
);
has 'temperature' => (
is => 'rw',
isa => 'Value',
);
has 'humidity' => (
is => 'rw',
isa => 'Num',
);
has 'pressure' => (
is => 'rw',
isa => 'Num',
);
after '_registerObserver' => sub {
my $self = shift;
my $thisObserver = shift;
push @{ $self->observers }, $thisObserver;
print "Now has: ", scalar @{ $self->observers }, " observers.";
};
after 'removeObserver' => sub {
my $self = shift;
my $deletionIndex = shift;
my $cutObserver = splice @{ $self->observers }, $deletionIndex, 1;
$cutObserver->clear_Subject;
print "Now has: ", scalar @{ $self->observers }, " observers.";
};
after 'notifyObservers' => sub {
my $self = shift;
foreach my $thisObserver (@{ $self->observers }) {
$thisObserver->update($self->temperature,
$self->pressure,
$self->humidity);
}
};
sub measurementsChanged {
my $self = shift;
$self->notifyObservers;
}
sub setMeasurements {
my $self = shift;
my ($temperature, $pressure, $humidity) = @_;
$self->temperature($temperature);
$self->pressure($pressure);
$self->humidity($humidity);
$self->measurementsChanged;
}
What have we achieved? We have composed the 'Subject' role into the'WeatherData' class and added a slot that lends this class a container for subscribers. Note how the notification is applied by iterating over contents of 'observers'. Note also that notification is initiated by the method 'setMeasurements' of the Weather Station, the chain of command being: 'setMeasurments' to 'measurementsChanges' to 'notifyObservers'. Now it remains to define the display. And that's coming up in Part 2.
~ * ~
All code in this series may be downloaded from:
http://sites.google.com/site/sanjaybhatikar/codeunquote/designpatterns-1
http://sites.google.com/site/sanjaybhatikar/codeunquote/designpatterns-1