Class-Std-Slots

 view release on metacpan or  search on metacpan

lib/Class/Std/Slots.pm  view on Meta::CPAN

    $ob1->do_stuff;

=head1 DESCRIPTION

Conventionally the ways in which objects of different classes can interact with
each other is designed into those classes; changes to that behaviour require
either changes to the classes in question or the creation of subclasses.

Signals and slots allow objects to be wired together dynamically at run time in
ways that weren't necessarily anticipated by the designers of the classes. For
example consider a class that manages time consuming downloads:

    package My::Downloader;
    use Class::Std;
    {
        sub do_download {
            my $self = shift;
            # ... do something time consuming ...
        }
    }

For a particular application it might be desirable to be able to display a progress
report as the download progresses. Unfortunately C<My::Downloader> isn't wired to
allow that. We could improve C<My::Downloader> by providing a stub function that's
called periodically during a download:

    package My::Downloader::Better;
    use Class::Std;
    {
        sub progress {
            # do nothing
        }

        sub do_download {
            my $self = shift;
            # ... do something time consuming periodically calling progress() ...
        }
    }

Then we could subclass C<My::Downloader::Better> to update a display:

    package My::Downloader::Verbose;
    use base qw(My::Downloader::Better);
    use Class::Std;
    {
        sub progress {
            my $self = shift;
            my $done = shift;
            print "$done % done\n";
        }
    }

That's not bad - but we had to create a subclass - and we'd have to arrange for it
to be created instead of a C<My::Downloader::Better> anytime we want to use it. If
displaying the progress involved updating a progress bar in a GUI we'd need to
embed a reference to the progress bar in each instance of C<My::Downloader::Verbose>.

Instead we could extend C<My::Downloader::Better> to call an arbitrary callback via
a supplied code reference each time C<progress()> was called ... but then we have to
implement the interface that allows the callback to be defined. If we also want
notifications of retries and server failures we'll need still more callbacks. Tedious.

Or we could write C<My::Downloader::Lovely> like this:

    package My::Downloader::Lovely;
    use Class::Std;
    use Class::Std::Slots;
    {
        signals qw(
            progress_update
            server_failure
        );

        sub do_download {
            my $self = shift;
            # ... do something time consuming periodically emitting
            # a progress_update signal like this:
            for (@ages) {
                $self->do_chunk();
                $self->progress_update($done++);
            }
        }
    }

and use it like this:

    use My::Downloader::Lovely;

    my $lovely = My::Downloader::Lovely->new();
    $lovely->do_download();

That behaves just like the original C<My::Downloader> example. Now let's hook up the progress
display - we're using an imaginary GUI toolkit:

    use My::Downloader::Lovely;
    use Pretty::ProgressBar;

    my $lovely = My::Downloader::Lovely->new();
    my $pretty = Pretty::ProgressBar->new();

    # Now the clever bit - hook them together. Whenever the
    # progress_update signal is emitted it'll call
    # $pretty->update_bar($done);
    $lovely->connect('progress_update', $pretty, 'update_bar');

    # Do the download with style
    $lovely->do_download();

We didn't have to subclass or modify C<My::Downloader::Lovely> and we didn't have to clutter its
interface with methods to allow callbacks to be installed.

Each signal can be connected to many slots simultaneously; perhaps we want some debug to show
up on the console too:

    use My::Downloader::Lovely;
    use Pretty::ProgressBar;

    my $lovely = My::Downloader::Lovely->new();
    my $pretty = Pretty::ProgressBar->new();

    # Now the clever bit - hook them together. Whenever the
    # progress_update signal is emitted it'll call
    # $pretty->update_bar($done);
    $lovely->connect('progress_update', $pretty, 'update_bar');

    # Add an anon slot to display progress on the console too
    $lovely->connect('progress_update', sub { print 'Done: ', $_[0], "\n"; });

    # Do the download with style
    $lovely->do_download();

Each slot can either be a subroutine reference or an object reference and method name. Anonymous
slots are particularly useful for debugging but they also provide a lightweight way to extend
the behaviour of an existing class.

Only classes that emit signals need use C<Class::Std::Slots> - any method in any class can be
used as a slot.

=head2 Signals?

The signals we refer to here are unrelated to operating system signals. That's why the class is
called C<Class::Std::Slots> instead of Class::Std::Signals.

=head2 Further reading

Sarah Thompson has produced a generic signals and slots library for C++:

L<http://sigslot.sourceforge.net/>

The accompanying documentation includes an excellent exploration of the benefits of signals and slots.

Qt (C++ again) uses signals and slots extensively. Consult the Qt documentation and in particular
the section on signals and slots for more information:

L<http://doc.trolltech.com/3.3/signalsandslots.html>

Other UI toolkits including NextStep / Cocoa / GNUStep use mechanisms similar to signals and slots
in all but name.

=head1 INTERFACE

C<Class::Std::Slots> is designed to be used in conjunction with C<Class::Std>. It I<may> work
with classes not based on C<Class::Std> but this is untested. To use it add
C<use Class::Std::Slots> just after C<use Class::Std>

    package My::Class;
    use Class::Std
    use Class::Std::Slots           # <-- add this
    {
        signals qw(                 # <-- add this



( run in 0.551 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )