Lets suppose that I have a class which represent an image and has a number of methods.
class Image
{
circle(x,y,radius,color);
square(x,y,w,h,color);
floodfill(x,y,color)
clear();
}
Furthermore, I want to have undo functionality. A simple way of implementing this is to keep a list of all the actions that have been performed. When I undo, I just re-run all of the actions. One solution would be to implement an aspect, something like this:
aspect ImageUndo
{
on Image::circle, Image::square, Image::floodfill
precall(object, args)
{
object.actions_list.add(args)
}
}
Essentially, this aspect has now modified the behavior of Image. That gives me concern. In particular, another programmer unfamiliar with the existence of ImageUndo aspect may run into the following problems:
- He adds a method, and it does not work with the undo functionality.
- Attempting to debug the undo mechanism, it is not clear where the actions_list get added to.
On the other hand we could have
class Image
{
@undoable_action
circle(x,y,radius,color);
@undoable_action
square(x,y,w,h,color);
@undoable_action
floodfill(x,y,color)
@undoable_action
clear();
}
Which does not bother me as much because it gives an idea of where to look for the undo code and makes is so that the new coder will probably notice it an automatically add it to a new method.
To summarize: aspects (at least those like the one I showed) seems to bring "implicit magic" into what code does. It seems to me that the implicitness is dangerous and we should really make it explicit.
Are there good reasons for the implicitness? Do people who actually use AOP write code that does this sort of modification?
Note: this is a reworking of Are certain problems solved more elegantly with AOP? which was closed because my previous version came across, unintentionally, as ranting.