Ask described four methods for incorporation of extensions: mixins, multiple inheritance, delegation, and hooks. Mixins, he claimed, are the best option for frameworks, allowing new methods to be added directly to the base classes that programs will be using. See Class::DBI::Plugin for an example of this technique.
Multiple inheritance has problems with encapsulation and generally makes it easy to shoot yourself in the foot (which I’ve seen brought up several times in the context of method resolution order on #perl6, though they seemed to have settled on a pretty good compromise for Perl 6’s default MRO). Of course, as Ask points out, Catalyst uses MI, and the Catalyst team seems happy with it.
Delegation, in which the main program assigns certain tasks to other objects (or even subprocesses), tends to be a sledgehammer — it’s very hard to make small tweaks without writing a lot of code.
That leaves hooks, in which plugins register with the main program to handle various predefined tasks. While there can be an initial pain to add hooks all over the place, Ask claims that this will pay off in finer control and a clear API for extensions.
Most of the rest of the talk followed qpsmtpd through several iterations of its hook-handling model. Each iteration did more work implicitely, making plugins successively easier to write. qpsmtpd eats its own dog food by trying to do as much as possible using plugins right out of the box. At this point it seems to merely have a small chunk of core functionality and a pile of hook management magic to make plugin code simpler.
qpsmtpd’s hook model was originally based on Apache / mod_perl hooks and return codes. For every hook, all registered handlers are called in sequence until one of them returns a code indicating that it has completely handled the hook. With recursion properly handled by the core hook handling code, even logging plugins that trigger normal plugins that call logging plugins Just Work. At this point, qpsmtpd even allows the result of a plugin to be caught and mangled by another hook, and primitive plugin inheritance is even possible.
Key in all of this is “Make it fun.” A large dose of sugar, a clean design, and a semantic model with great depth for hacking, are all intended to make plugin writing fun, so as to attract more hackers and build a community around the project. On that note, Ask pointed us to smtpd.develooper.com, svn.perl.org/qpsmtpd/, and develooper.com/talks/ for more information.
What’s your favorite extensibility technique? Why?