There's more than one way to do things | |
PerlMonks |
Aspect-Oriented Programming: Couple of Short Examplesby chunlou (Curate) |
on Aug 05, 2003 at 09:40 UTC ( [id://280934]=perlmeditation: print w/replies, xml ) | Need Help?? |
Suppose you have a bunch of scripts or modules like the following +--------------+ +-----------------+ | Script A | | Script B | +--------------+ +-----------------+ | insert_alias | | insert_Nickname | +--------------+ +-----------------+ where they insert "alias" into the database for the users from various applications. Each insert subroutine does basically the same thing, though the actual subroutines' names varies. After all the applications have been up and running for a while, now you decide that you don't want any user to use "Saint" as an alias anywhere in the system due to new business rules. Are you going to modify each insert subroutine script by script? Well, perhaps. But, hopefully, not too much... if you use Aspect-Oriented Programming (AOP), like the following example (just another way to do things): +----------------------------------------------+ | Aspect Pre-Condition | +----------------------------------------------+ | advice(calls(qr/main::insert_.*/), sub{...}) | +----------------------------------------------+ ^ | +----------------------+ | | +--------------+ +-----------------+ | Script A | | Script B | +--------------+ +-----------------+ | insert_alias | | insert_Nickname | +--------------+ +-----------------+ Let's try to impose the no-saint pre-condition check on this "alias" script:
and this "Nickname" script:
With AOP, we only have to add to two lines to the "alias" script.
And this is the pre-condition aspect module that will impose the no-saint restriction.
In the module, advice() will check what subroutines to look for, as determined by calls($spec), and what action to take, as defined by $subc, once a subroutine is found. calls() signifies the fact that action by $subc will be taken before a matching subroutine does anything at all--hence precondition. Which subroutine advice() will take action on is determine by $spec, which is just a regex matching package::subroutine. The subroutines to be matched are all of them called directly and indirectly by the your module or script. So specifying packages in the regex is very important. In a script, you will use something like qr/main::.*/ at least. Notice the definition of the anonymous subroutine $subc where $::thisjp->signature(@_) returns the matching subroutines along with the values passed to it, something like insert_alias('Mike'), and $_[0] or @_ are the one coming from the matching subroutine, so for insert_alias('Mike') matched by calls($spec) the $_[0] will be 'Mike'. Now let's look at the "Nickname" script.
Notice you can turn the aspect on and off by pre_insert()->enable and pre_insert()->disable respectively. One handy use for this is you can turn an aspect on or off automatically depending on, say, whether you're on production or development environment by looking up the environment variable. We've done the pre-condition. Let's try a post-condition. Let's say now you don't want to interfere with the users. But instead you merely want to be notified if a user uses 'Saint' as an alias. The code for the post-condition aspect module more or less has the same structure. Instead of calls(), now we use returns() in order to intercept a matching subroutine after it has done its work and returns its result. And we define the anonymous subroutine used by advice() accordingly. The changes in the script are minimal, two lines. Here
and there.
If OOP is a vertical view of a system according to its object hierarchy. AOP is a horizontal view across common concerns. AOSD is a good place to learn more about AOP.
Back to
Meditations
|
|