http://seancorfield.github.io for newer blog posts." />

An Architect's View

CFML, Clojure, Software Design, Frameworks and more...

An Architect's View

Mixins...

January 12, 2006 · 18 Comments

All of a sudden folks are talking about mixins in ColdFusion. Joe Rinehart blogged a technique using <cfinclude> inside a CFC to bring in common methods and I have to say I really don't like that very much. Somehow an include inside a CFC just "smells" bad to me (and, yes, I'm well aware that Ruby achieves mixins through an 'include' directive). Robin Hilliard showed a mixin technique ages ago (October 2004) that used a class / method to inject the mixed-in methods and that appeals more to me. So I figured I'd develop my own solution and offer that. I've left component.cfc alone on my systems for a long, long time, on the grounds that it's a "system component" and shouldn't be messed with (and, hey, it might get overwritten in an upgrade). However, this seemed like a really good use for modifying that "universal base class"... Edit WEB-INF/cftags/component.cfc so it looks like this:
<cfcomponent>
   
   <!--- use cfscript so functions are hidden from metadata --->
   <cfscript>
      function mixin(type) {
         var target = createObject("component",arguments.type);
         structAppend(this,target);
         structAppend(variables,target);
      }
   </cfscript>
   
</cfcomponent>
To test it, let's have a simple CFC with a couple of public methods we want to use (mixin):
<cfcomponent displayname="test.mixin.mixed">
   <cffunction name="one" access="public">
      <cfreturn "I'm one">
   </cffunction>
   <cffunction name="two" access="public">
      <cfreturn " and I'm two!">
   </cffunction>
</cfcomponent>
And here's the CFC where we use it:
<cfcomponent displayname="test.mixin.plain>
   <cfset mixin("test.mixin.mixed") />
   <cffunction name="test" access="remote">

      <cfreturn one() & two()>
   </cffunction>
</cfcomponent>
I made that method remote so I could invoke it through the browser rather than have to write a CFML page just to test it: http://localhost/test/mixin/plain.cfc?method=test (that will hit your own localhost setup!). It mixes in just the public methods of the target class (so be warned that they can't depend on non-public methods in the target class!). It puts the methods in both this scope and variables scope so that name lookup works correctly (either just trust me that you need to do this or experiment for yourself!). It overwrites local methods with those from the target class (easy to change - structAppend() takes a third, optional, boolean argument that controls that). Thoughts?

Tags: coldfusion

18 responses so far ↓

  • 1 Simon Horwith // Jan 12, 2006 at 2:49 PM

    I saw Joe's post but didn't get around to commenting on it. I have to agree with you, Sean. 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.
  • 2 Mike Cohen // Jan 12, 2006 at 3:06 PM

    What's the advantage of using this technique over composition?
  • 3 Sean Corfield // Jan 12, 2006 at 3:16 PM

    Mike, with composition you'd have to do explicit delegation:

    composedObject.someMethod()

    whereas the mixin arranges for someMethod() to actually become part of your primary object at runtime. That means the API of the primary object changes to reflect the mixed in methods - and so client code can call those methods on your object without you needing to write proxy method stubs.

    In fact exactly the same thing could be achieved through proxy generation / injection which is something I believe ColdSpring will be implementing. Well, almost exactly the same. Proxy generation mirrors the proxied API whereas mixins augment the API.

    Make sense?
  • 4 Spike // Jan 12, 2006 at 3:50 PM

    Maybe I'm getting old and cynical, but this whole mixin idea just sounds like a maintenance and debugging headache waiting to happen.

    I'd prefer to live with the explicit delegation in the vast majority of cases.
  • 5 Mike Cohen // Jan 12, 2006 at 4:03 PM

    Yeah, I was looking at your calls to one() and two() inside of test() and thinking, ok, so you've saved calling mixed.one() and mixed.two().
  • 6 Sean Corfield // Jan 12, 2006 at 4:53 PM

    Mike, the point is that the public methods of the mixin become public methods of the primary object so *client* code can call the methods as-if they were hand-coded methods of the primary object.

    The mixin blends the public APIs of the two classes. You don't have to write test.mixin.plain.one() and have it delegate (to mixed.one()). It's essentially method injection.

    If you only wanted to use the functionality of the mixin in order to implement your own methods then, yes, composition and internal delegation would be fine.
  • 7 Joe Rinehart // Jan 12, 2006 at 5:57 PM

    I'm actually 90% with Spike on the whole thing. I think Mixing is very limited in terms of good uses, and has nearly unlimited headache potential.

    Sean, I started to write a reply about why I've gone with &lt;cfinclude&gt; instead of mixin() here, but as I wrote it, I realized I don't like either way very much, but just find &lt;cfinclude&gt; the lesser of two eveils. This led to a conclusion I wanted to share more widely, so I've posted it on my blog.

    Simon, please feel free to chime in with why using &lt;cfinclude&gt; any further defeats the purpose of &lt;cfcomponent&gt; than using mixin().

  • 8 Joe Rinehart // Jan 12, 2006 at 5:59 PM

    Oops, forgot this. The entry I just mentioned is at clearsoftware.net/index.cfm?mode=entry&amp;entry=C16E8B2D-E081-2BAC-69A405B4EBEB1F85
  • 9 Scott Barnes // Jan 12, 2006 at 6:50 PM

    If you want some constructive crit, ask some Flash Developers on their thoughts with regards to Mix-ins.

    A large portion of the FLEX framework uses it to provide componentry with say, &quot;event&quot; dispatching for one.. without having to write the logic for each individual piece of the puzzle.

    It can also, via runtime bolt on &quot;logic&quot; when needed, vs &quot;always&quot; having it in place.

    On the whole, mix-ins smell at times like interfaces, yet not all the glory that comes with an interface.

    I've not seen a use for them yet in Coldfusion that warrants them but i'm open minded to see some further contexts of how it may benefit us all.

    Just be mindful, that i've personally found mix-ins hard to debug and test at times - yet - i'm not sure that could be the case with regards to CFMX, as its still quite procedural in its flow (Flash can thread nicely, where CFM has to wait for sub-logic to finish before it can continue onward..unless you use event gateways).
  • 10 Matthew Lesko // Jan 13, 2006 at 5:36 AM

    Bruce Eckel has an in depth discussion of this particular topic - albeit in a Java/C# context - that covers a lot of ground.

    I thought one of the most interesting ideas presented there was the concept of &quot;Traits&quot; - primitive units of code that can be used as the building blocks of a class.

    Bruce Eckel Forum Thread
    www.artima.com/forums/flat.jsp?forum=106&amp;thread=132988

    Traits White Paper: www.iam.unibe.ch/~scg/Archive/Papers/Scha03aTraits.pdf
  • 11 Jared Rypka-Hauer // Jan 13, 2006 at 8:03 AM

    As Hal explained this to me over the phone a few days ago, mixins are a way to provide something like both interfaces and multiple inheritance in CFCs. It goes something like this:

    Given 3 classes: Person, Student, and Teacher

    Person implements your garden variety behaviors like run(), walk(), and sleep(). But Student and Teacher both have to implement different versions of goToClass() (on heads to the main desk at the front, the other calls findASeat(), or in my case sneakInLate())

    If you have a running Person object, you can call:

    createObject(&quot;component&quot;,&quot;Student&quot;).initialize(Person)

    This will inject the Student version of the appropriate behaviors, as well as any other Student-specific behaviors, into Person, which from then on is considered a Student. Since it walks, sounds, and swims like a Student, we can be all duck-typish about it and consider it a Student.

    The only thing that it doesn't cover is the lack of restrictions on CFCs, so you can't force CF to guarantee that your Student really is a Student other than by checking for signatures in your own code to verify the type of Person your dealing with.

    HTH...

    J
  • 12 Patrick McElhaney // Jan 13, 2006 at 10:03 AM

    Sean,

    I'm shocked at how easily you bent on the &quot;don't mess with component.cfc&quot; rule.

    The cfinclude method smells just fine to me. It can be dropped into any existing CFC. And it shouldn't be that hard to figure out what it does, even if you've never heard of mixins. There are no caveats like only mixing in public methods. There's no third-party code that could be different (or missing) from one machine to the next.

    One major difference I see is your mixin is &quot;dynamic&quot; -- it can be applied at runtime -- whereas the cfinclude mixin is &quot;static.&quot; I'm all for dynamic typing. I'm not so sure about dynamic mixins.

    Patrick

  • 13 Jared Rypka-Hauer // Jan 13, 2006 at 2:32 PM

    *shivvers*

    Mixins via include... limited, disencapsulated (I made a new word!), and potentially a support nightmare (injecting private methods is a scary, scary idea). I vastly prefer the idea of injecting live methods from one class into another class. You're not dependent on the CF server's parser to determine whether or not a class is allowed, you can do the whole thing from outside the class (a factory reads both classes and injects the one into the other), you're not dependent on calling cfinclude multiple times to achieve your ends (which simplifies the process in general, and allows you to handle all of this from outside the object in question)...

    It just strikes me as a better solution overall...

    Laterz!
  • 14 Josh King // Jan 13, 2006 at 4:28 PM

    Sean, I have a feeling you're going to bolt up in bed in the middle of the night and scream &quot;I can't believe I suggested that!&quot; :)

    It's clever, and I have to agree that using &lt;cfinclude&gt; inside a component has a funky smell, but changing component.cfc is just asking for trouble.
  • 15 Sean Corfield // Jan 14, 2006 at 3:56 AM

    I figured if other people were offering ugly solutions, so would I! :)

    Jared has offered a variant on mine, using a 'universal base class' (for mixins) which also smells (and which doesn't even blend the public interface as far as I can tell - haven't tried the code yet).

    Hal previously offered a similar (and, IMO, more complex) approach.

    The fact that all of these solutions smell in one way or another should tell us something about mixins. Even in languages with great support for them (e.g., C++) they tend to smell really badly and, for the most part, can always be implemented in a less smelly way...

    Patrick, actually Joe's cfinclude *is* a dynamic mixin too - cfinclude is a runtime construct, not a static one (it dynamically adds the contents of the included file where it is encountered at runtime). It is not a source code include like C / C++.
  • 16 Patrick McElhaney // Jan 14, 2006 at 7:06 AM

    Sean,

    cfinclude many be a runtime construct, but the way it's used in Joe's example, it's more like a compiler directive. The cfinclude is only called once, when the CFC is created. So you can only mixin when the object is created. You can't add mixins to existing objects.

    But don't take this as an endorsement of Joe's method. Joe's method doesn't scare me as much because it's easily done and undone. But I'm still skeptical about mixins in general.

    I'm glad that we finally stopped prentending CF is Java. Let's not start pretending that it's Ruby. :)

    Patrick



  • 17 Sean Corfield // Jan 14, 2006 at 9:07 PM

    Patrick, my example also only calls the code once when the CFC is created. My point is that people need to be careful not to get into the mindset that cfinclude is somehow a compile-time directive - it absolutely is not. Consider this code:

    [cfif someCondition]
    [cfinclude template=&quot;#someVariable#&quot;]
    [/cfif]

    At *runtime* the contents of the included file are executed, which may result in additions to 'variables' scope. Remember that a function declaration is *executed* - the result is the addition of a new *variable*, named for the function, that contains a reference to the compiled class file that represents the function (as an object).
  • 18 Robin Hilliard // Jan 17, 2006 at 3:46 AM

    Hi All,

    Interesting comments here. Firstly Sean's implementation of mixins is a lot less convoluted than my original method - I was trying to share a single original mixin object that I could use as a sort of static scope for all the mixin's clients, and that made it all a bit too complex to inflict on others or indeed myself. After explaining how it worked to a guy at Gruden and having him say &quot;why not just use includes&quot; I kept asking that question myself.

    I should explain at this point that my broader philosophy regarding CF was and continues to be RAD, RAD, RAD - after some herculean efforts with a framework to emulate interfaces in CF, automatic persistence etc and thinking &quot;why am I not just using Java?&quot; I decided that RAD was what CF was about (and also the only truly unassailable benefit of CF when I was out selling it as an Allaire/Macromedia Sales Engineer). The reality also is that for every Model-Glue/Mach-II CFer out there there are ten happy Fuseboxers and a hundred who think a framework is something you grow tomatoes on - and they need our (self-professing-architect-dudes) help.

    So shoot me, I've been using plain cfincludes for a while as mixins, really to see if my natural revulsion for the idea actually manifests itself as a real disadvantage. So far it seems to be working rather well, although I have to wash my hands a bit more thoroughly after using them...

    Part of my research has involved reading about and experimenting with Ruby. Mixins (modules) are a core part of the Ruby language - for instance the Enumerable module adds implementations of all the iterator methods (inject, collect etc) to any class with an each() method. This is real code reuse - and I don't see it introducing any particular headaches for Enumerable's users.

    If there's any language out there vying for CF's all-important RAD crown then Ruby would have to be a front-runner, particularly with the success of the Rails project. We should be learning what we can from Ruby, evaluating it in the cool light of day and using the good bits. If it makes development, debugging and maintainance easier, it works for me, good OO or no.

Leave a Comment

Leave this field empty