Three ways to introduce othogonal behavior: Aspects, Method modifiers, and Subroutine attributes
In my previous post I spoke about what Subroutine attributes are and how to use them. Here I compare them to other techniques for introducing orthogonal behaviors: Method modifiers and Aspects. These three techniques enable to modify what a subroutine does, but they do it in different ways and are thus suited for different needs.
If you don't know what Subroutine attributes or Method Modifiers are, please read my previous post.
Aspect Oriented Programming
Aspect Oriented Programming (AOP) is a third way to modify how some subroutines behave. It enables to declare in one separate file which subroutines will be modified and how. It can be good for Logging for example: one file to declare all the subroutines to be logged, and what to log for each. Wouldn't such a file give you a great overview of what you Log? Logging is a separate concern: it should be handled in a separate place. That is what Aspects are for.
The CPAN alternative is MooseX::Aspect. Its Synopsis is simpler to understand, and the syntax is more friendly. But it is less powerful, as you don't have access to the context and stack trace of the modified subroutine (unless if I missed this in the doc?). But you might not need that anyway. Any example of where that could be useful?
So, which technique should I use?
Let me sum up what I think is best, so you can all correct me :-)
If you need to modify a few subroutines in one file:
Method modifiers are the simplest solution,
But if you feel that the modification should be indicated where each subroutine is, then you want Subroutine attributes. These might also be useful if each subroutine is modified a bit differently, by passing arguments to the subroutine attribute.
If you want to modify subroutines in several files:
If the modified methods in a few classes have similar names, then you could use Method modifiers in a Role. Because that's what Roles do: adding some chunk of behavior to some classes;
If you aim at totally different subroutines in totally different places, then Subroutine attributes are best if you want to indicate the modification next to each subroutine...
... and Aspects are best if you want to indicate all the modifications in one single file, to easily get an overview of all subroutine modifications.
Does this all make sense?
Another way to put it
Any technique used to modify what some subroutines do, should be used to induce orthogonal behaviors: behaviors that don't necessarily belong in the subroutine, but should happen each time the subroutine is called. Such behaviors can be Logging, Debugging, Plugins, Authorization systems (some subroutines can only be run by some people), Memoization, Exceptions. Any more ideas?
For introducing behavior at the application level (eq: logging), Aspects might be the best choice. In this case you care about being able to manage the behavior in only one place.
For introducing behavior at the class level (only to some specific classes), Method modifiers might be best, possibly in a Role. This is especially appropriate if the behavior needs its own attributes and methods, which can all be declared in the role.
For introducing behavior at the subroutine level, then go for Subroutine attributes. In this case, what is important for you, is the ability to easily change whether the a subroutine is affected, at the place where the subroutine is defined.
One more thought: there is another practical difference. Subroutine attributes can be passed arguments and they know the context; Aspects know about the context and the stack trace. This might also influence your choice.
A naive list of applications that each tool might be best for
This list is up for discussion, please :-) I would use...
Logging: to have an overvierw of everything you log, in a single file
Method modifers in Roles, for:
Plugins: because they can be added to some group of classes
A lot more stuff. Because they enable to add attributes and methods to any class, Roles are used for everything nowadays, which is great. See the list at the end of this post.
Subroutine Attributes, for:
Debugging: to very easily tell a subroutine to give us some information about what it is doing and when, without needing to step away from my code: just add this "DebugArgs" Subroutine attribute to dump the subroutine's arguments.
Memoization: because any subroutine anywhere might want to do it. (see Attribute::Memoize). And I generally don't need to keep track of it
And I am really not sure which tool is best for an Authorization system. It depends on the structure of your app, and whether you want to easily see an overview of authorizations (with Aspects), block access only to subroutines which are in a few particular classes (with Roles), or block access to very specific subroutines which are spread a bit everywhere in the code (but in this case I would probably use Aspects, to gather the information in a single place).
And now that I wrote this last paragraph, I think that it could apply to many of the other aplications.
What do you think?
Here are some useful Roles that you can find on CPAN (I don't have the time to write a description for each right now) : MooseX::Role::Matcher, MooseX::Role::Debugger, MooseX::Role::DBIC, MooseX::Role::LogHandler, MooseX::POE::Role, MooseX::Event, MooX::Role::POE::Emitter, MooX::Role::Pluggable, MooseX::ConfigFromFile, MooseX::Getopt, MooseX::Role::MissingMethodUtils, MooX::Attributes::Shadow::Role, MooseX::Role::Tempdir, Throwable, Throwable::X, Throwable::Error, Throwable::Factory, MooseX::Role::Timer, and much more.