DuckTyping, Interfaces, CFEclipse, and how I write code

Posted At : February 1, 2007 6:47 PM | Posted By : Mark Drew
Related Categories: coldfusion, quack, cfeclipse

My previous post about CFEclipse and DuckTyping got me thinking about how I develop applications.

In a couple of systems I have written, I create a number of services. What do I mean by this? Well, a service (in my terminology, I am not CS graduate, just an old hacker) in my code is a single component (singleton) that returns objects (such as pages, products, users, you get the idea).

So for example, lets take a simple PageService:

<cfcomponent displayname="PageService" output="false">
      <cffunction name="getPage" returntype="any">
         <cfargument name="pageid" required="false">
            ... some code ...

         <cfreturn pageObject />
      </cffunction>
   </cfcomponent>

This component returns my pages which is all fine, but lets say, I am going to change the way I get pages (for some crazy reason) and instead of using a database, I want to use a file based storage, or an XML file. Instead of changing it, I create a couple more services:

  • XMLPageService: gets pages from XML
  • DBPageService: gets pages from a Database (my original service)
  • FilePageService: gets pages from a FileSystem

Now, how do I instantiate these services? Well, I create a PageServiceFactory. The PageServiceFactory's role is to get me the right type PageService, with a function such as:

<cffunction name="getPageService" returntype="Any">
      <cfargument name="servicetype" hint="type can be:db,xml or file">
         <cfswitch expression="arguments.servicetype">
            <cfcase value="db">
               <cfreturn getDBPageService() />
            </cfcase>
            <cfcase value="xml">
               <cfreturn getXMLPageService() />
            </cfcase>
            <cfcase value="file">
               <cfreturn getFilePageService() />
            </cfcase>
            <cfdefaultcase>
               <cfthrow message="unknown page service requested">
            </cfdefaultcase>
         </cfswitch>
      </cffunction>

This is a great example of DuckTyping for programming and not just the for ( the celebrity harlot which is) performance since, when I do my call, in my controller I call the Factory asking for a Page as follows:

<cfset myPage = PageServiceFactory.getPageService('db').getPage("some page")>
   <cfset title = myPage.getTitle()>
   <cfset content = myPage.getContent()>

I am able to call the same method on each service even though the service type can change.

One of the requirements for duck typing to work in your code is that all the methods that you would expect to be there, ARE there (otherwise you get a function not found exception). You would have to either very rigorously test your code or setup unit tests that all of the PageServices have to pass.

In Java, you can define an interface that all the PageServices comply with, basically the interface is a contract that the PageServices must follow with the functions and arguments that they have. Most java IDE's and editors, whilst you write your class, will alert you that you are not conforming to this contract and provide ways to overcome this.

This kind of functionality I would love to put in CFEclipse, if ColdFusion had interfaces.

How would the code change? The getPageService function in my Factory would then have a returntype of "IPageService", which would be my Interface, defining what functions all my PageService's have. In CFEclipse, whilst you are writing a new PageService, you would say it implements the "IPageService" interface. If you missed out any functions, CFEclipse would warn you that your new Service is not compatible, all before you have even saved the file.

Now you could have function insight when you type "getPageService("db")". This wouldn't be DuckTyping anymore, of course, but it would definitely remove a lot of the architectural mistakes that you could do in your application.

Or have I missed something?

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Derek P.'s Gravatar I really like this approach...we can only hope that the next version of CF gives us interfaces though...or perhaps you could do something with extends...hmm..

Also, if you are using duckTyping..you are probably doing it for some performance reasons right? You may also want to reconsider using cfcase then as well and replace it with cfif statements...its a bit faster I've been told.
# Posted By Derek P. | 2/1/07 9:53 PM
Christopher Bradford's Gravatar The easy solution to your example, Mark, is to have an IPageService CFC that all your PageServices extend, and then CFE can provide the insight for that IPageService (getPageService() would return IPageService). Of course, this won't work if your CFCs need to "implement" multiple "interfaces".
# Posted By Christopher Bradford | 2/1/07 10:21 PM
Mark Drew's Gravatar @Derek: Actually DuckTyping is only really performance realted in CF, I was trying to show WHY you would use it programatically and how I used it, and you are right, it would be faster if we used cfif or if we converted the "db", "file","xml" to integers.

I shall blog about how you can do this in CF, whilst maintainging the "text" and the dangers of it.

@Christopher: I dont like using Extends in this case, its not really defininging what the Services are. I already use extend in the Services for the init and setConfig methods (they are being instantiated from ColdSpring) so in this case, I would have to have two extends, and thus performance (not the point of this post though) would be even more decremented.
# Posted By Mark Drew | 2/1/07 10:42 PM
Az's Gravatar Well I've heard rumors that Interfaces are on the list of new features for the next version of ColdFusion, but I'm not sure if that is true or not. Have you signed up for the beta program Mark?

With all of the work you're doing on CFEclipse, I think you should definitely get on the beta so you plan future enhancements for CFEclipse to go along with the new version of ColdFusion. The better the tools are at the time of release, the easier it'll be to code for all those new features.
# Posted By Az | 2/2/07 12:28 AM
tony petruzzi's Gravatar @chris

Do I read your comment and though .Net.
# Posted By tony petruzzi | 2/2/07 1:58 AM
Jim's Gravatar Hmm. I'm just getting started in Mach-II (OO) so don't kill me if this is wrong - but I think several of the Mach-II examples use something very similar to configure the application to use a variety of databases (MSSQL, MySQL, Access, etc)... I've kind of been ignoring this as I don't need this functionality but now I'll have to go back and look again.
# Posted By Jim | 2/2/07 2:02 AM
Mark Drew's Gravatar @Jim: Mach II is probably doing exactly that, this is the factory pattern a well known programming design pattern.
# Posted By Mark Drew | 2/2/07 8:44 AM
Tom Chiverton's Gravatar This is a great example of a place where interfaces would be useful.
Also, you could plug additional IPageService's in via ColdSpring, for instance, just to make CFE's job harder :-)
# Posted By Tom Chiverton | 2/2/07 9:54 AM
Mark Drew's Gravatar @Tom: I actually do that, but I re-wrote this post a number of times to keep it on target, I thought throwing in ColdSpring would confuse people, the <cfreturn getDBPageService() /> could actually be something like getBeanContainer.getBean('DBPageService') but if we had the returntype as "IPageFactory" we could have insight in CFEclipse (hey, this doesn't exist! I need to write it) but you get the idea.
# Posted By Mark Drew | 2/2/07 10:06 AM
Scotty's Gravatar Hi Mark, Im late to this conversation but I was just catching up on reading :)

so...
Not that this will help CFEclipse at all but one of the cool things about ColdFusion is the "Duck-Typing". Did you know you can even modify the inheritance tree on the fly? I did a proof of concept type thing a while back.... ok, a looonnngg while back. The biggest problem with modifying the inheritance tree at runtime is 1) performance 2) The way Coldfusion seems to cache class instances.

http://www.franciswscott.com/blog/1/2005/11/CFImpl...

Has a link to the actual working files. Though not a good example of what you would use interfaces for :)
# Posted By Scotty | 2/10/07 1:27 AM
Marburg's Gravatar I think you will have a hard time implementing this in CFEclipse, mainly because Interfaces not only work on requiring certain methods to be implemented, but also on the number of arguments passed in and their data type. And unfortunately cf doesn't support method overloading.
# Posted By Marburg | 4/19/07 7:16 AM
??l?'s Gravatar Do I read your comment and though .Net.
# Posted By ??l? | 5/8/07 6:07 AM
A4?'s Gravatar Nice Site !
# Posted By A4? | 7/28/07 6:30 AM