Jan 12 2006

Model-Glue, "Mixins," and Headstart

Posted by Joe Rinehart at 5:30 AM
13 comments
- Categories: ColdFusion MX | Model-Glue

When Model-Glue 1.1 hits, you might notice something weird in the "Headstart" application template: a <cfinclude> tag in the middle of a CFC file. There's a story behind that, and it's a practical example of what people are calling a "Mixin." From what I can tell, the formal name "Mixin" is coming from the Ruby camp, and it's catching on: it's supposed to be supported in C# 3.0. Well, we've had them since...the dawn of CFCs, to my knowledge.

I have...umm..."mixed" feelings over Mixins. In the world of "is-a" and "has-a," mixins are sort of a "can-do," which is why I name mine the way I do. They're likely to be used and abused, and I can see where overuse may make life hellish - but I think that's true for most programming tools.

Ok, hubbub aside, here's a practical use and example of a Mixin:

In the Model-Glue "Headstart" template I'm working on, I've got five controllers. They all use at least one CFC that gets autowired to the controller via ColdSpring. Some of them, like EmailController and ErrorController, use an EmailService CFC. So, I wind up with code like this duplicated in both Controllers:

<cffunction name="SetEmailService" access="public" returntype="void" output="false">
   <cfargument name="EmailService" required="true" type="any" />
   <cfset variables._emailService = arguments.EmailService />
</cffunction>
<cffunction name="GetEmailService" access="private" returntype="any" output="false">
   <cfreturn variables._emailService />
</cffunction>

Well, that's nice and redundant. In fact, there are three such services and a configuration management CFC that get used by the various Controllers; some use just one, some use more than one. However, they all have something in common: they're using application-level services to get something done.

Let's say each controller "can-do" use of application services. Wouldn't it be nice if there was a way I could just have all of repeated code in one place?

One approach is inheritance. ColdFusion (thankfully!) doesn't have multiple inheritance, so that's out. All of the controllers currently inherit from modelglue.core.Controller, so I could create a different subclass of modelglue.core.Controller, and point the actual controllers to that new subclass, but that gives me a code smell: I'm inheriting to share functionality, not represent an is-a relationship.

Instead of using inheritance, I'll just "mix in" that functionality.

It's, uh, really simple.

First, I take all of that redundant code, and put it in my mixin file. I'll call it UseApplicationServices.cfm, and put it under model/mixin.

Second, I'll copy all of those redundant UDFs into UseApplicationServices.cfm.

Third (and last), I'll replace all of that redundant code with the tag that started it all, <cfinclude>>:

<cfinclude template="/headstart/mixin/UseApplicationServices.cfm" />

What we've done

We've used a Mixin to say that each Controller "can-do" functionality related to public setting and private getting of application-wide services. If I need to add a new service, I can just define it in my ColdSpring XML, add a SetNewService method to my UseApplicationServices.cfm Mixin, and all of my "Mixed" Controllers now have access to it. Pretty trick, eh?

Conclusion

Mixins are powerful, but they can be somewhat dangerous. You're adding methods to your CFC on the fly, and it could become a fun jungle of figuring out just where a specific method came from: imagine multiple mixins with identically named methods!

Comments

barry.b

barry.b wrote on 01/12/06 9:35 AM

Joe, I'm curious:

you're not tempted to &quot;inject&quot; a CFC with ApplicationServices into your other CFC's? are you sure you reall want a cfinclude?

... ah bugger! it seems Robin's beaten me to the suggestion...(comment above)
Tim Blair

Tim Blair wrote on 01/12/06 9:41 AM

We use a similar thing in an application framework we've written to enable us to use &quot;filters&quot; that are run before/after a given controller action. For example we have a User.cfc controller which has this in it:

&lt;cfscript&gt;
addBeforeFilter(filter=&quot;authenticate&quot;, except=&quot;login,auth,logout&quot;);
addBeforeFilter(filter=&quot;haveID&quot;, only=&quot;edit,update,delete,show&quot;);
&lt;/cfscript&gt;

Of course, the functions addBeforeFilter() and addAfterFilter(), plus the &quot;runner&quot; function, must be added to the component (in this case via inheritence, but could be another &quot;mixin&quot;). These functions perform a &lt;cfinclude&gt; to pull the extra code into the component at run time (e.g. authenticate.cfm), then the &quot;runner&quot; fires the appropriate function calls at the correct point.

Doing this means we can reuse code across components and also means we don't have to repeat code within a component.

It's certainly very useful, but as you say needs tight control or it could easily end up with a mess of includes; our &quot;filters&quot; are usually just small functions which perform a very specific action.
Joe

Joe wrote on 01/12/06 9:41 AM

Lol...yeah, it's tempting. Robin's looks similar to what I'm doing, except that it copied methods from one CFC into another.

I think you're suggesting a more traditional, compositional approach where my controller has-a ApplicationServices CFC that exposes get/set methods for those services. It'd work, too.
Paul Carney

Paul Carney wrote on 01/12/06 10:11 AM

Gotta love it when you finally find a name for something you have been doing for a while...

We have some large sets of function code that we not only re-use in a few places, but also wanted to separate our code files so that the CFCs stayed short-and-sweet. We've been putting many of those larger functions into include files and then using cfinclude in our CFCs.

It works great and gives us more granular control over the code. Yes, it could be a mess, but we use specific file-naming conventions to help.

I never thought it was a bad thing to use includes in CFCs. It made sense.
Keith Gaughan

Keith Gaughan wrote on 01/12/06 11:32 AM

Mixins, in name and concept, have been around for a very long time and predate Ruby quite a bit. In what C++ programming I used to do in the past, I used them quite a bit. The term itself originated in MIT, and came from an early Lisp object system. Here's what the &lt;a href=&quot;http://c2.com/cgi/wiki?MixIn&quot;&gt;Patterns Wiki&lt;/a&gt; has to say on them and &lt;a href=&quot;&quot;&gt;Wikipedia&lt;/a&gt; too.
Kurt Wiersma

Kurt Wiersma wrote on 01/12/06 1:25 PM

So basically you could call this method injection. My guess is at somepoint ColdSpring will support method injections so that you don't have use cfinclude inside your CFC.
Chris Scott

Chris Scott wrote on 01/12/06 2:45 PM

Well... as a matter of fact, mixin support via ColdSpring's aop is something I am definitely planning on implementing in the near future. Man, here I go thinking about how this will be done and not talking about it, and all of a sudden everyone's talking about mixins and I'll be late in the game. Oh well. Mixins will be coming to ColdSpring, that's good enough!
Sean Corfield

Sean Corfield wrote on 01/12/06 3:36 PM

Just a caution / caveat:

&quot;Before writing a class that uses a mixin, you should carefully consider the fact that their use generally violates the principals of good object-oriented coding. Although mixins can be a simple and powerful way to add functionality to custom classes, they can also introduce dependencies and overhead that you can avoid by carefully designing your custom object hierarchy.&quot;

http://www.macromedia.com/support/documentation/en/flex/1/mixin/mixin2.html

I didn't write this - but I do agree - and here's some stuff about mixins I wrote back in the mid/late 90's:

http://corfield.org/index.cfm/event/cplusplus.section/section/compile4

http://corfield.org/index.cfm/event/cplusplus.section/section/compile5

These cover &quot;classic&quot; mixins in C++ where you needed to re-blended two classes that had a common base class somewhere in the hierarchy. In particular the second article talks about pushing common functionality into a separate class that is the reblended back into the derived class.

I look forward to seeing this sort of blending support in ColdSpring because something about simply including the file into a CFC gives me the creeps... I can't put my figure on it, but seeing a cfinclude inside a CFC just &quot;smells&quot; bad to me... Robin's approach - explicit runtime mixins - smells better although obviously it's more machinery.
Chris Scott

Chris Scott wrote on 01/12/06 3:51 PM

I feel very similarly to the use of mixins as I do towards aop. If you are working with or configuring it's use in code, it quickly can lead to headaches tracking down hidden functionality. That's why I like the declarative approach so much. It gives you a high-level overview of 'extra' behavior that is appended to components. Even though coldspring aop can be configured entirely in code, I've never shown anyone (even dave!) how to do it, because of this reason. I would hate to see people struggling with methods performing hidden functionality with no where to look for answers. Open up the offending cfc and there's nothing there, you need to remember that your configured the behavior somewhere! Even coldspring isn't the place you do your configuration of mixins, I would really like to see it done in a way that is easily trackable.
Joe

Joe wrote on 01/12/06 9:01 PM

Hi all,

My comments here and on Sean's blog started to get long and duplicate content, so I've just posted a follow up to this post at:

http://clearsoftware.net/index.cfm?mode=entry&amp;entry=C16E8B2D-E081-2BAC-69A405B4EBEB1F85
Lola Lee Beno

Lola Lee Beno wrote on 01/13/06 6:08 AM

Auuggghh. Going to be fun trying to debug codebase full of mixins. Another reason why I don't use cfinclude too often.

Write your comment



(it will not be displayed)