Implementing AOP in CF using mixins
Usually the most profound ideas are sleepers. They don’t sound like much when you first hear about them. Sean made a comment about methods during his Duck Typing presentation last week that I think we may still be finding new uses for this time next year.
He said that in ColdFusion, methods are "first order citizens". What does this mean? It means you can perform operations on them in the same way you can perform operations on attributes (although the permissible operations will be constrained by the value type – I doubt you could multiply two methods!).
What can we use this for? Ask me this time next year, but one example of the breadth of uses for this would be to implement Aspect Oriented Programming using an in-class decorator.
As I described in my last post, ColdSpring uses a decorator pattern to implement AOP by automagically wrapping your classes with proxies which then decorate them with your advice (the code you want to add before, after or around the method calls).
It just came to me that an "in-class decorator" would be an example of how flexible mixins might be. This isn’t solving a problem that I have right now, so I haven’t thought much about it, but I want to see people posting some ideas on just how creative we could get with mixins, so I thought I’d throw this out here as a starting point.
A decorator describes a pattern where you wrap a class with another class that exposes the same interface (methods, arguments and return types) but adds additional behavior. So, strictly an in-class decorator is an oxymoron. But if you’ll permit me some leeway (in the spirit of duck typing: if it quacks like a decorator, it’s a decorator), imagine if you used mixins to dynamically rename all of the methods of a class and added a new set of methods including your advice and calls to the renamed original classes. You would now have a class where you’d extended the behavior of all of the methods with additional responsibilities at design time – yes, you’d have written a self-decorating class!!!
How might this work in principle? Lets take a simple UserService with save() and delete() methods. You want to add logging to both by calling a LoggingService, and you just wanted to log the ID of any item that was deleted or saved.
Your UserService would look something like:
<cffunction name="init" returntype="UserService">
<cfreturn THIS>
</cffunction>
<cffunction name="Save">
<cfargument Name="User" type="User">
UserDAO.Save(ARGUMENTS.User);
</cffunction>
<cffunction name="Delete">
<cfargument Name="User" type="User">
UserDAO.Delete(ARGUMENTS.User);
</cffunction>
</cfcomponent>
If you wrote a mixin that renamed init() as init_original(), save() as save_original() and delete() as delete_original() and then added init(), save() and delete() methods (which called the original methods and then made the call to the logging service), you would have implemented an "in-class decorator" using mixins!
This just seemed like a cool idea, but don't worry - my next few posts will be much more practical. I'll return to building the framework - showing how to design an interface for the Model and to build up some base classes for data access and validation.
I’ll leave you with Sean’s most specific comment (you have to scroll down) on how exactly the "first class citizen" functionality of methods works. It is pretty fascinating stuff!
"The first class citizen status of functions isn't strictly speaking something that comes from the dynamic typing of ColdFusion. ColdFusion components compile to Java classes. ColdFusion functions also compile to Java classes. A component instance has references to an instance of each method declared in the component. A user-defined function in any page is also compiled to a Java class."
"Within the page, a reference to that function is actually a reference to an instance of the class that the function compiled down to. That means you can write a UDF and assign it to the public scope of a component - and the late binding of variable names means that name lookup will be done in the context of the call, i.e., inside the component (even tho' the function was written outside the component)."



Any input on your experience much appreciated!
Best Wishes,
Peter
"CFCs are basically 3 structs with UDFs in them... variables, this, and super. Their methods aren't examined but by name at this point, so allowing overloading isn't really possible at the moment and to do so could require the runtime to work harder. Even if they got that part to work, since "methods" on a CFC are UDFs in a struct key, it would break all the rules of CF variables to allow 2 variables with the same name (you have to think of UDFs as data members as much as executable code). Maybe they can do it without adding too much overhead to the process, but I don't know if I think it's worth it."
Best Wishes,
Peter