Prototype Based Programming with CFCs

A while ago, I posted an example of creating CFC definitions on the fly using a function I wrote called StructToBean().

It essentially used the concepts of Prototype Based programming to implement the getter and setter methods on a blank CFC, so that the data of the Struct was encapsulated in an object without having to actually write the object itself.

I put that post on here while I was writing a library that was an attempt to cut down on the amount of time I spent writing simple Beans for use as Business Objects.

The basics of the library was such that I had a XML config file that defined each of the Beans that I wanted to be able to use in the system, like so (cut down version):

<compoundObjects>
<package name="Address">
<object name="Street">
<property name="number" type="numeric" />
<property name="name" type="string" />
</object>
</package>
</compoundObjects>

The library would then create a .cfm file on the fly, which would contain all the UDFs required to add the required getters and setters to the blank CFC.

When the specified Bean was called for, the library would create a blank CFC, and insert each of the methods onto the blank CFC at runtime, so it was ready to use.

As personal projects tend to do, this idea fell by the wayside for a while, and now I've been rebuilding compoundtheory, it's come back to me, and I've started to question whether or not it's really something I want to get behind in terms of how I build my CF applications. I'm still pretty much sitting on the fence as to whether or not I'm actually going to rewrite the code (it needs a good refactor), and utilise it in the new version of CT.

While CFCs aren't exactly prototype based, the basic flexibility of being able to utilise slots in the CFC to add and remove methods remains the same, but the question that I am wondering at the moment, is should it be used.

On one side there are alot of interesting things you can do with method injection and removal:

  1. Adding Object functionality on the fly
  2. Create Interfaces on the fly
  3. Faking Base class functionality
  4. Changing protection on methods due on the fly (i.e. remove a init() constructor once it has been called once)
  5. Fix bugs on the fly

But obviously there are cons as well:

  1. Adds a whole level of complexity to an application that is harder to document
  2. Breaks some core OO concepts (which may not be a bad thing, but you have to be happy about it)
  3. Can lead to some awefully spagettied code if not managed correctly

Now, obviously, whether or not you leverage this capability is very dependent on applicaiton requirements, your own personal views on OO Analysis and Design etc, but the usage of this functionality does exist, yet seems to be a topic that is not talked about much in the CF community.

So I ask you guys, as an information gathering excercise:

  1. Have you considered taking a Prototype based approach before?
  2. If you had, did you do it?
  3. If you did, what was it, and why did you do it? (I would love to hear of some places it was used!)
  4. If you didn't, why not?
  5. Would/wouldn't you consider using this approach for something in the future?

That being said, in the next few weeks (as I continue to develop the next version of CT), I'll do a follow up post on whether or not I decided to utilise the prototype based approach to developing the Business Objects for this site, and why I decided to do it, and hopefully this will get some interesting discussion going!

Some more reading on Prototype Based Programming

Leave a Comment

Comments

  • Tim Lucas | June 17, 2005

    Augmenting CFC instances with functionality at runtime I can see a point for, as you can still create and modify your domain logic in the CFC file, but I don’t see much point in creating CFCs totally from scratch at runtime. If you wanted to override a getter or setter, or add other methods to the object, where would you do it?

    I followed your last post with quite an interest as recently I started a new project and after being spoilt with ruby on rails was looking for a nicer way to create business objects from db tables.

    I considered using the create temp file + cfinclude approach you suggested but decided that it was going too beyond CF’s capabilities (i.e. too much of a hack).

    In the end I went with a static bean approach, where I create a bean cfm generated from the db schema (at the moment just by hand), and cfinclude that into my business component.

    The component then ends up looking like:

    <cfcomponent name="Member" extends="activerecord" output="false">
    <cfscript>
    table_name("members")
    </cfscript>

    <cffunction name="group" returntype="Group">
    <cfreturn createObject("component","Group").find_by_id(getGroupId()) />
    </cffunction>

    <cffunction name="has_role" returntype="boolean">
    <cfargument name="role" type="string" required="true" />
    <cfreturn ListContainsNoCase(getRoles(),arguments.role) />
    </cffunction>

    <cfinclude template="member_bean.cfm" />
    </cfcomponent>

    Ideally I’d love a way to create the ‘group’ association method just by adding the following to the top of the CFC:
    <cfscript>
    belongs_to("Group")
    </cfscript>

    … but just couldn’t come up with a nice way to do it.

    I think CF is really the wrong tool for the job if you’re looking at doing much runtime/meta/prototype based programming.
    Sure it’s possible to get partly there using code generation or mixing in methods by playing with scopes, but w/o proper language support you’ll be limited as to what you can accomplish and it’ll always be an unsupported ‘hack’.

    For my own sanity i’ll stick with the static approach for client projects in CF and keep the runtime wrangling for python and ruby.

    Keep up the great posts on your experiments!

  • Mark | June 17, 2005

    Thanks for your feedback there Tim!

    For a minute there, I didn’t think I was going to get any response at all!

    I hear you on the trying to find a nicer way to create BO’s – hence the idea for building CFC definitions this way.

    I totally hear what you are saying in regards to it being a ‘hack’ and not a documented feature (heh – but I know how much that has stopped me before). That is one thing that is holding me back at the moment. (That and looking at how much work I think I’m going to need to refactor my original code).

    The original plan for the project was also to provide a configurable BO mapper, for the ease of being able to convert a query into a series of BO’s. etc, and also tools for turning a DB schema into the XML file that defines the BO’s in the system.

    The more I think about it – I think I’ll write this project, just to see where it leads. When the new version of CT comes out, I’ll be releasing a heap of stuff as open source, so that should be in there as well for people to look at.

  • Tim Lucas | June 17, 2005

    Yeah there doesn’t seem much interest in these kind of techniques in CF. The only other interest in CF runtime wrangling that I’ve noticed is Robin Hilliard’s mixin stuff.

    What’s the advantage of your XML syntax over MM’s XML cfc syntax?

    <object name="Street">
    <property name="number" type="numeric" />
    <property name="name" type="string" />
    </object>

    VS

    <cfcomponent name="Street">
    <cfproperty name="number" type="numeric" />
    <cfproperty name="name" type="string" />
    </cfcomponent>

    You could then add more business logic to the BO definition, and have your code generator sprinkle the init, accessors, CRUD and all the rest in there for you.

    If you wanted to be able to define other logic to be auto generated (such as validation rules and associations) you could maybe get away with defining it after the cfproperty’s as long as ‘output=false’ was declared.

    For example:
    <cfcomponent name="Street" output="false">
    <cfproperty name="number" type="numeric" />
    <cfproperty name="name" type="string" />
    <cfproperty name="postcode" type="numeric" />
    <cfproperty name="suburb" type="Suburb" />
    <cfproperty name="houses" type="Array" />

    <database table="streets" datasource="mydsn" />
    <validate_range_of property="postcode" between="1" and="1000" />
    <belongs_to property="suburb" type="Suburb" foreign_key="suburb_id" />
    <has_many property="houses" type="House" />

    <!— custom business logic —>
    <cffunction name="emptyHouses" returntype="Array">
    <!— cfreturn the empty houses —>
    </cffunction>
    </cfcomponent>

  • Mark | June 17, 2005

    The idea was to simply cut down on the amount of code authoring neccessary for authoring Beans.

    So the added advantage of my syntax is just that there is a little less of it – pretty much.

    One could also argue with some snippets/automation tools you could probably accomplish the same thing, and you would be right as well.

    The idea of declaring it inside the CFC itself is a clever one, and using reflection to work out how to decorate the Bean appropriately. (getMetaData() can be very expensive operation however).

    That’s given me some stuff to think about – primarily what is the most elegent way of getting adding the getter’s n’ setters on a CFC with a cfproperty.

    I see 2 options – (1) through an abstract base class, so the bean fires off a generateMethodsFromProperties(), or (2) Pass it to a CFC that does reflection on the given CFC and then adds the methods externally.

    Both have their pros and cons…

    I don’t know if (anyone) would build these ideas – but they are definatley interesting.

  • Tim Lucas | June 20, 2005

    Here’s a somewhat releated post from Scott Barnes, with an interesting comment from Robin Hilliard:
    http://www.mossyblog.com/archives/479.cfm

  • Mark | June 21, 2005

    Also a related post on the Rocketbooks site for discussion of mixins:

    http://www.rocketboots.com/blog/index.cfm?mode=entry&entry=6E1A91AC-E081-51EF-A796B568EF7FABEA

    Also note the post from Sean Corfield about persistance frameworks.