Interfaces in ColdFusion - I think I have finally come around on CFINTERFACE

ColdFusion

I will admit it...

When all the pre-ColdFusion 8 debate was swirling about whether or not to add interfaces into CFML, I didn't really get it.  It's true.  The only OOP I had ever really done was in CFML, ever having experienced how interfaces really come into play, and I think I fell into the the easy path of considering it worthless because I didn't take the time to understand it.  So very quietly, I was in the "we don't need it!" camp.

Much has changed for me as a developer since that time.  I have gotten quite comfortable in Flex development, and working with Flex made me realize that Java is not really all that difficult to grasp for me now, and I am pretty comfortable getting around there too.  I have begun playing a bit with Groovy as well, so I have been exposed to much more since there actually *was* a debate about interfaces.

So now that debate has long since died, and the pro-interface crowd won  - and then immediately decided that they didn't actually need interfaces after all - I realized yesterday that I finally get it!

Before I go any further, I would like to give my definition of an interface, because I am sure there are still plenty of CF developers who are where I was just a short time ago with just a very loose grasp, if any, on the concept.  In fact, I am not sure that I am much beyond a loose grasp, but I will do my best to explain.  For those who already know all this, just bear with me here.

In talking about interfaces with another developer on my team yesterday he asked how they are used.  Half joking, I replied "They aren't!  They don't *do* anything at all".  What I meant by this is that you don't ever actually call an interface and your application never actually asks an interface to do anything.  The interface exists simply to enfore rules in your data model.  In essence it is a zero-tolerance "Object Gestapo" that makes sure that no one ever gets out of line.  An interface is truly little more than a contract that says "Any object that implements me must contain the things I define".  In essence, I suppose you could think of it as a shell CFC that defines methods, arguments, and return types, that you can't actually call.  If I then define a CFC that implements that interface, it better damn well adhere to it strictly or the object gestapo comes down on you hard!

So why this extra work right?  CF is a loosely typed language. Its status as an OOP language has been debated.  However, the fact of the matter is that most of the top slice of CFML developers are writing some really amazing object oriented applications.  Due to this, some of the more strict products from languages like Java might actually have some place in our work.

Let me give a real world solution that might help solidify my swirling thoughts on the matter.  to give background on the environment I am working in, I recently began working for a relatively large insurance provider.   Counting another 17 developers that are just being moved into ColdFusion from other languages, we have one of the larger ColdFusion development groups that I am aware of in my area.  Many of the developers work remotely, and we have several that are in other states.  Due to this, communication must be clear to keep everyone on the same page.

A current project that is in the works is to move our entire policy system off of one third-party vendor to another. The APIs between the two are vastly different, however, we still need the same data available to us in our applications.  To make things more complicated, we will be moving one state at a time.  So effectively there will be two distinctly different policy systems in play until the last state is converted.

My architectural approach to solving that problem is to use a PolicySystemFactory that returns the appropriate PolicySystem to me based on specific business rules.  Any PolicySystem will actually serve as an adapter to the specific API of its third-party system, but the methods that the PolicySystem object exposes to the application  *must* be consistent.  The application shouldn't really care which system it is talking to as long as the system is giving it the right data and doing taking the right action on data that is passed in.

How do we ensure that we have the exact same API for not only the two current policy systems, but any future policy systems that we don't even know about yet? 

Well, we could just make sure all developers know about it, and write it in some document hundreds of pages long that no one ever reads.  Then after both of those things fail, the developers will eventually figure it out as the application pukes in different areas as it is getting data in different shapes than it is expecting, or as it tries to use methods differently than the PolicySystem is expecting.  Eventually they will work this out.

Or... You could do both of the above items (communicate & document) and save everyone a lot of trouble by establishing that any PolicySystem *must* adhere to rules established in an interface.  By taking this approach, there is no question whatsoever that the PolicySystem will not break your application by working differently than expected.

How do you implement this approach? (really.. I didn't mean it as a pun)

First, we would want to figure out exactly how a PolicySystem should be shaped.  In our ultra-simplified example, a PolicySystem must have three methods: getPolicy(id) returning a Policy object, getPolicyAgent(Policy) returning and Agent object, and getPoliciesByStatus(StatusId) returning a structure of Policy objects

This is what our Interface would look like:

PolicySystemInterface.cfc

<cfinterface>
	
<cffunction name="getPolicy" access="public" output="false" returntype="Policy">
	<cfargument name="id" type="numeric" required="true" />
</cffunction>
	
<cffunction name="getPolicyAgent" access="public" output="false" returntype="Agent">
	<cfargument name="Policy" type="Policy" required="true" />
</cffunction>
	
<cffunction name="getPoliciesByStatus" access="public" output="false" returntype="Policy{}">
	<cfargument name="StatusId" type="numeric" required="true" />
</cffunction>
	
</cfinterface> 


As you can see above, we use the <cfinterface /> tag to define this CFC as an interface.  We then write our functions much like we would in a <cfcomponent /> keeping in mind that whatever we define must be used *exactly* the same way by any object that implements it.  So what would one of those objects look like?  Here is our old legacy policy system object.  Take a look at what is going on in line #1 in the <cfcomponent /> tag.

OldLegacyPolicySystem.cfc
<cfcomponent name="OldLegacyPolicySystem" output="false" extends="AbstractPolicySystem" implements="PolicySystemInterface">

<cffunction name="init" access="public" output="false" returntype="AbstractPolicySystem">
	<cfreturn super.init("OldLegacyPolicySystem") />
</cffunction>

<cffunction name="getPolicy" access="public" output="false" returntype="Policy">
	<cfargument name="id" type="numeric" required="true" />
	<!--- NOTE - In real life, we would use a service to create this bean, not instantiate it here!!! --->
	<cfset var Policy = CreateObject("component","Policy").init() />
	
	<!--- 
	########################################
	In this area we would access policy info using the OldLegacyPolicySystem (however it does it!) and populate the Policy bean 
	########################################
	--->
	
	<cfreturn Policy/>
</cffunction>

<cffunction name="getPolicyAgent" access="public" output="false" returntype="Agent">
	<cfargument name="Policy" type="Policy" required="true" />
	<!--- NOTE - In real life, we would use a service to create this bean, not instantiate it here!!! --->
	<cfset var Agent = CreateObject("component","Agent").init(arguments.Policy) />
	
	<!--- 
	########################################
	In this area we would access Agent info using the OldLegacyPolicySystem and populate the Agent bean 
	########################################
	--->
	
	<cfreturn Policy/>
</cffunction>

<cffunction name="getPoliciesByStatus" access="public" output="false" returntype="Policy{}">
	<cfargument name="StatusId" type="numeric" required="true" />
	<cfset var PolicyStruct = {} />
	<!--- 
	########################################
	In this area we would load Policies using the OldLegacyPolicySystem and build a structure of them 
	########################################
	--->	
	<cfreturn PolicyStruct />
</cffunction>
</cfcomponent>


You should notice that we have:
 extends="AbstractPolicySystem" implements="PolicySystemInterface"

Two things are happening there.  First, we are extending an AbstractPolicySystem object that will hold common functionality among all PolicySystems.   Ours looks like this:
<cfcomponent output="false">
<cffunction name="init" access="public" output="false" returntype="AbstractPolicySystem">
	<cfargument name="SystemName" type="string" required="true" />
	<cfset this.SystemName = arguments.SystemName />
	<cfreturn this />
</cffunction>
</cfcomponent>


For our test case, when we instantiate our PolicySystem we can quickly prove which one we are dealing with by looking at PolicySystem.SystemName.  That said, your application shouldn't know or care.


By adding the implements attribute we are ensuring that this PolicySystem plays by the rules we defined in our PolicySystemInterface.  No "ifs" "ands" or "but"s!

So, this is the exercise that brought me around on CFINTERFACE.  What are your thoughts? 

Steve Withington said:
 
Ok, this is probably one of the better real-world examples of why someone _might_ want to implement a ColdFusion Interface. However, unless you're dealing with the types of issues you're trying to address, a CF Interface still doesn't make sense in many other scenarios to me.
 
posted 245 days ago
View Replies (1) || Add Comment Reply to: this comment OR this thread
 
.: HIDE REPLIES :.
 
I could not agree more. In a huge majority of ColdFusion applications it would be completely unnecessary.
 
posted 245 days ago
Add Comment Reply to: this comment OR this thread
 
Henry Ho said:
 
I'm using CFInterface for the first time on this project I'm working on. I'm actually quite comfortable using it.

I'm not sure if I'm using CF 'the right way', since I like to have everything typed, and cfinterface is more or less, more checking, although not by the compiler.

One thing I'm not sure I'm glad or I missed is when you pass in the object as an interface, CF would let you invoke not just the methods in cfinterface, but any methods. This is one aspect of interface that's quite different from JAVA if I'm not mistaken. The security of the consumer of the interface is not enforced.
 
posted 245 days ago
Add Comment Reply to: this comment OR this thread
 
Steve said:
 
I'll be honest and say that I don't really see how this is doing anything but adding an extra file. I don't presume to know anything about interfaces yet, but from my untrained eye it just looks like the cfinterface duplicates the arguments for the methods they represent. How is it any different than if there was no interface at all?
 
posted 245 days ago
View Replies (3) || Add Comment Reply to: this comment OR this thread
 
.: HIDE REPLIES :.
Henry Ho said:
 
cfinterface vs. duck-typing.

IMO, cfinterface is better at enforcing consistent API, provides additional documentation at code level, and easier to translate some of the well-known design patterns.
 
posted 245 days ago
Add Comment Reply to: this comment OR this thread
 
 
Steve, hopefully I am not stating the obvious here but what Dave's example is showing that whatever code consumes the PolicySystem will always work regardless of which System is actually returned from the PolicySystemFactory.

I say "work" as in that we know that the methods and their arguments will be consistently named/typed across all implementations of the PolicySystem.

It also probably helps to keep other developers from putting conditional logic where it shouldn't be (to call a different PolicySystem for example).
 
posted 245 days ago
Add Comment Reply to: this comment OR this thread
 
 
As I said about "They don't *do* anything at all" in the sense that you don't access it or call methods on it. What it does is provide a clear path for development to ensure that any object that implements PolicySystemInterface abides by a clearly defined set of rules. It is about enforcement, not action. In a small system, I can't imagine much of a need in most cases. However, in a big enterprise environment, where multiple groups could be working on multiple solutions, this is a clear way to force everyone to be on the same page.
 
posted 245 days ago
Add Comment Reply to: this comment OR this thread
 
Steve said:
 
Ok, I think I understand. Sorry for my ignorance, my OO understanding is novice at best I think.
 
posted 245 days ago
Add Comment Reply to: this comment OR this thread
 
edomgroup said:
 
Right on Dave!

To reply to many of the thoughts above, the point in interfaces is not that they should be all over the place in your code but that they act as a tool for making systems of objects more generic or at least extendable.

I can come up with many scenarios that interfaces apply and most are oriented less towards day to day development and more towards extendable frameworks and transition code as Dave has here.

Validation frameworks where different types of validation can be added by users of the framework are another simple example, products in a shopping cart that can have many different properties but need the same methods to be displayed in listing pages, etc. and there are countless more.

What I don't understand is why people would rather write the hundred page document or a non-standard page-long comment that the developer is likely to let fall out of memory instead of making them bonk into the interface and go "Oh, that's what I need" before they've introduced problem code. It works great to copy and paste a new implementation of the interface as well.
 
posted 245 days ago
Add Comment Reply to: this comment OR this thread
 
James Morrow said:
 
Nice post, Dave.

This has actually brought me around to considering using CFInterface in some of the things we do here.

For example, we have several versions of a SecurityAdapter implemented for different environments (DBSecurityAdapter for local dev work, LDAPSecurityAdapter, ADFSSecurityAdapter, etc.). It becomes obvious that this is an obvious situation where we can see benefit from implementing a common interface, to ensure that the SecurityManager can always rely on being able to call the same methods and get returned the same variable type.
 
posted 242 days ago
Add Comment Reply to: this comment OR this thread
 
Steve said:
 
I just did some strategy pattern exercises in Java and was still having trouble understanding the "why" of interfaces. Why do I need them, how do they impact my development. Then I remembered Dave's post here. When I re-read the below quote it all clicked.

"Any object that implements me must contain the things I define"

Fascinating things interfaces... I do want to say though, that I disagree that this is only for large teams or huge apps. There is no reason as a solo dev with a small app I shouldn't use interfaces. Who better to keep from taking shortcuts than the solo dev?

Anyway, great post Dave (yeah, I know it's "old").
 
posted 177 days ago
Add Comment Reply to: this comment OR this thread
 

Search