Jan 12 2006

ColdFusion Mixins: For now, I'll CFInclude.

Posted by Joe Rinehart at 8:42 PM
5 comments
- Categories: ColdFusion MX | Model-Glue | Causing Trouble

Last night, I blogged a way of doing "Mixins" in ColdFusion that involved the <cfinclude> tag. Mixins in ColdFusion aren't new, and I wasn't expecting my post to be perceived as an increase in the community's interest in their use. I'm not sure it's any higher now than it was a few days ago. However, Simon Horwith seems to think using is a horrible idea altogether. That's ok with me, but I'd like to know why. On Sean's blog, he comments:

"I think that this is very bad form. I encourage anyone who reads this comment to please not include files inside of your CFCs. It defeats the whole purpose of using components."

In his post, Sean suggests a Mixin implementation in which a mixin() method is used to copy method from a source CFC instance into the current CFC instance. While I do think <cfinclude> has an odor to it, I've though about it all evening, and this approach gives me a "smell" as well. Here's why:

First, I think this method does more than <cfinclude> to "defeat the whole purpose of using components." (I don't think either actually completely defeats the purpose.)

The purpose of a component is to provide a template from which something (an instance) can be, well, instantiated. This something is an Object, capable of containing data and functionality that operates on that instance's data.

However, a Mixin, by itself, has no instance data. In fact, a Mixin, in the Ruby-esque, dynamically-typed-language form that I've encountered them, is a collection of functionality that may use existing instance data, but can't have any of its own.

If there's a way to defeat the purpose of using a component, it's by creating a component that can't, by definition, be what a component is (a collection of functionality and instance data)!

In short, I don't think a Mixin should be something that can be instantiated; it has no instance.

(We could probably argue a bit about Abstract classes and methods a bit here, but that's way outside of the scope of ColdFusion, so can we skip it?)

Second, there's no clean way to make a mixin() method available to classes at large. You'd either need a universal base class (not a good idea), or to take Sean's approach of modifying the web-inf version of component.cfc. That's the base of all CFCs, and is created when ColdFusion is installed. I'm not a big fan of having to modify it, because its creates a customized version of ColdFusion on your machine. That's OK if you can get away with it, but many, such as those creating COTS software, just can't do such things.

Third, the actual implementation of a mixin() method could be problematic. Sean addresses one in his entry: what do you do if the method you're trying to mix already exists? It could be optional to overwrite it, controlled by an argument. (Using <cfinclude> provides a layer of safety, but does limit mixin capability. If you try to redefine an existing function, ColdFusion won't even instantiate the component.) I've also seen someone suggest that the mixin() method provide "safe" namespaces for methods to be copied to (like myMixin_doSomething()).

Further, what about public vs. private Mixins? There are cases where the mixed functionality may be an internal utility - should it always go to both variables and this scopes? How do we control this? With <cfinclude>, there's no new caveats; you can just use the access attribute on the <cffunction> tag.

While <cfinclude> may be more limited in terms of capability, it is existing, stock ColdFusion functionality, not open to multiple implementations, and therefore a standard API that's not likely to change.

<cfinclude> - not without problems

Using <cfinclude> has its own problems. I haven't seen anyone voice my strongest problem with it: the mixin is explicit, done at instantiation, and is therefore fairly rigid and static. If you use it, there's no (good) way to dynamically control what gets mixed at runtime.

However, with mixins, do we *want* people to conditionally mix? That's a situation I'd rather see an IoC container solve (yes, this is a hint to my real point in this post).

It also just "feels" weird. Earlier today, while talking about this over instant messenger, I told Sean that it "feels like kissing your sister." (Please don't get any odd, hillbilly ideas about my gene pool. I'm an only child, and can therefore only use the phrase as a hypothetical simile.)

Conclusion

I'm not happy with any of the current ways to mix in ColdFusion. At the moment, I lean towards <cfinclude> because I feel it's safest (no method overwrites, and lack of mixing capability), follows the KISS principle (no implementation debates), and it doesn't make me use Components that can't be true Objects.

However, I think there's a better solution than either <cfinclude> or mixin(): a third party to govern mixing mechanics. Hi, ColdSpring!

ColdSpring and Mixins

If you read the comments on my first post, you'll see that both Kurt Wiersma and Chris Scott, both involved in the ColdSpring framework, hint that we'll see some sort of Mixin support for ColdSpring. I'd really like to see this, as it'd alleviate some of my concerns:

  • Common Implementation

    Those doing things that use IoC and AOP are the most likely to need a Mixin, and ColdSpring could provide a third-party implementation of mixing mechanics that'd both set some rules (method overwrites?) and let you have wide mixing support without modifying ColdFusion itself.

  • Dynamic Mixing

    You'd mix only if appropriate, and have an IoC container that could help resolve what you really *want* to mix. If you were mixing in persistence methods, this could probably let you do something along the lines of mixing in database-specific persistence at runtime, overcoming a big problem with <cfinclude> based Mixins.

I don't see any way that ColdSpring could use .cfm files as the Mixin templates, so it'd probably use CFCs. That's a sacrifice I'd be willing to make.

Comments

Paul Carney

Paul Carney wrote on 01/12/06 9:20 PM

Joe, it's the common &quot;textbook vs. reality&quot; issue (a.k.a. the Ivory Tower syndrome) that is similar to database normalization. There are the &quot;pure&quot; ways to do things, but nothing is really ever implemented in real-life situations that do not take advantage of any opportunity available.

cfinclude does an excellent job in putting code in a place when either a script is being compiled or a CFC is being instantiated. It's simply one way of using the tools. As you stated, who cares (for the most part) where the code comes from - as long as it's manageable?

Those who blatantly tell people to ignore this are simply standing too high on their soapbox.
Chris Scott

Chris Scott wrote on 01/12/06 9:27 PM

OK, Joe, you asked for it! CSP-36 in jira = Expanded AOP support for mixins!
Sean Corfield

Sean Corfield wrote on 01/13/06 1:18 AM

Yeah, soon after posting my little piece about mixins I ended up expanding the code to private an option to mix private methods (into the variables scope only) but that required adding a method to expose the variables scope on any CFC which was *really* ugly so I haven't blogged the updated code! :)
Scott Arbeitman

Scott Arbeitman wrote on 01/02/07 3:30 AM

I take a slightly different approach. I think a better way is to encapsulate mixins is to wrap then in components which extend a base Mixin component, which has a mixin(target) method.

Then, to mix in methods into components, you put the mixin in the pseudo-constructor of the component. You no longer need edit any base component.

&lt;pre&gt;
&lt;cfset CreateObject(&quot;component&quot;, &quot;MyMixin&quot;).mixin(VARIABLES) /&gt;
&lt;/pre&gt;

Advantages of putting mixins within components compared to using cfinclude:
* inheritance of mixins. I'm not sure if this is really a good thing, but it might be.
* they can be unit tested using cfcUnit

Write your comment



(it will not be displayed)