NAME

interface - Define an API for use by a package

SYNOPSIS

Define an interface:

use interface;

interface My::API;

use interface qw(My::Other::API);

sub foo ($);
sub bar ($@);

Use an interface:

package My::Implementation;

use interface qw(My::API);

Other methods:

UNIVERSAL::implements( My::Implementation => 'My::API' );

DESCRIPTION

This package introduces a new Perl keyword, interface, that allows API declarations via subroutine prototypes.

Usually, an algorithm is written to require objects of particular types to make sure certain methods are available. This can tie certain programs to particular object frameworks, which might not always be the best way to write a program. This module tries to correct this by allowing interfaces to be defined without creating a class. These interface definitions are called `interfaces' after the Java concept (also known as `protocols' in Objective-C).

Interfaces not only allow more flexible tracking of implemented APIs, but can also aid in the debugging process during module development. Any subroutines that are prototyped in an interface are prototyped in the using package. For example, if package A uses interface B, and interface B defines subroutine C, then A::C will be prototyped. Perl will then issue warnings if the subsequent subroutine definition doesn't match the prototype given in the interface definition.

DEFINING AN INTERFACE

A interface is defined in the same way a package is defined, except using the interface keyword instead of the package keyword:

use interface;
interface My::SimpleIO::Interface;

sub read($);
sub write($;@);

(N.B.: The following paragraph is subject to change.) The default file name extension for interface files is .pi instead of .pm. If a .pi file can't be found, then Perl will look for a .pm file. Use .pi when you want the interface to be importable without requiring the loading of any code. Use .pm if code is loaded with the interface. Note that perldoc will not find pod that is in a .pi file. Place the interface documentation in a .pod file instead.

You can also sub-class interfaces by using them:

interface My::MoreAdvanced::IO::Interface;

use interface My::SimpleIO::Interface;

sub open($;$);
sub close($);

If you want to make sure a method is available but don't care about the prototype, you can simply declare it without a prototype:

interface My::Sans::Prototype;

sub foo;
sub bar;

Subroutines without prototypes in an interface won't have any effect except when you have an explicit check for the interface.

USING AN INTERFACE

Using an interface is simple:

use interface qw( interface list );

This will push the interface list onto the package global @INTERFACES and prototype any subroutines that have prototypes in the interfaces. If you want people to think you implement a particular interface without getting the benefit of the prototypes, then push the prototype name onto the @INTERFACES global without the use statement. Interfaces are not expected to load any code.

Note that a class is considered to implement the interface if any super-class implements the interface. The class does not gain the benefit of the prototypes unless it explicitely uses the interface.

TRACKING INTERFACE SUBSCRIPTIONS

Instead of requiring objects derived from a particular class, you now can check that the object implements a particular interface:

die "Need to be able to read and write"
     unless $object -> implements("My::IO::Interface");

This even works if $object's class doesn't know anything about interfaces. The implements method will look for the actual methods in that case and check their signature against the expected signature defined in My::IO::Interface. If no signature is expected, it checks for the method's existance using UNIVERSAL::can.

Interfaces can co-exist with packages. The package global @INTERFACES is used to track interface subscriptions similar to the way @ISA tracks class inheritance. A class has an interface if it is listed in the @INTERFACES array, is implemented in a super-class, or is implemented implicitely in the class.

BUGS

There are sure to be some. The filter is not the most robust and may be easily confused. This is a source filter. Some areas that need improvement:

  • Better error reporting (almost non-existent when defining interfaces)

  • Anything `out of the ordinary' can lead to undefined results (this includes code other than simple prototypes in the interface definition).

  • Any code mixed in to the interface definition is ignored. The interface definition is replaced by `1;' in the source that Perl sees after the filter. This also means that line numbers will be off.

  • Documentation.

AUTHOR

James Smith, <jsmith@cpan.org>

COPYRIGHT

Copyright (C) 2003 Texas A&M University. All Rights Reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.