Trying CFInterface...Not Seeing the Point
<cfinterface displayName="rdbms"> <cffunction name="init" access="public" output="false" returntype="mssql"> <cfargument name="dsn" type="string" required="true" /> </cffunction> <cffunction name="setDsn" access="public" output="false" returntype="void"> <cfargument name="dsn" type="string" required="true" /> </cffunction> <cffunction name="getDsn" access="public" output="false" returntype="string"> </cffunction> <cffunction name="getTables" access="public" output="false" returntype="table[]"> </cffunction> <cffunction name="getColumns" access="public" output="false" returntype="column[]"> <cfargument name="table" type="string" required="true" /> </cffunction> <cffunction name="translateCfSqlType" hint="I translate the RDBMS-specific data type names into ColdFusion cf_sql_xyz names" output="false" returntype="string"> <cfargument name="typeName" hint="I am the type name to translate" required="yes" type="string" /> </cffunction> <cffunction name="translateDataType" hint="I translate the RDBMS-specific data type names into ColdFusion data type names" output="false" returntype="string"> <cfargument name="typeName" hint="I am the type name to translate" required="yes" type="string" /> </cffunction> </cfinterface>
It's pretty easy to create and implement the interface and it does serve as a way to clarify the requirements a bit for people creating additional connectors. That's all fine and dandy, but what impact does this have on my application? Currently, the RDBMS-specific component is added via composition to a generic datasource component. I do this so that I only have to define one type of object within Flex for the automatic type translation (i.e. I only have a datasource.as rather than a mysql.as and a mssql.as). The generic datasource component gets and sets the RDBMS-specific type like so:
<cffunction name="setDbms" access="public" output="false" returntype="void"> <cfset variables.dbms = createObject("component","cfcgenerator.com.cf.model.datasource.#dsntype#").init(getDsnName()) /> </cffunction> <cffunction name="getDbms" access="public" output="false" returntype="any"> <cfreturn variables.dbms /> </cffunction>
In this case, the only thing that would change immediately would be the returntype of getDbms() could be set to IRdbms. Other than that, nothing changes. What improvement does this offer? Well, in theory it guarantees that the RDBMS-specific connector has implemented the proper methods required by my code. However, in reality, since there is no compiler and no type-checking it does no such thing. If I implemented the wrong methods in my RDBMS-specific component, ColdFusion will generate a runtime error the same as it would if I didn't use an interface at all and called a method that doesn't exist. The only difference is the type and location of when that runtime error is thrown (a point I know Sean has covered before).
Anyway, I am not saying I have written cfinterface off yet, but I am hoping some of its very vocal supporters might illuminate the point that I may be missing (if I am missing something). Without strongly-typed variables that need to be declared, it just seems like the interface has little to no impact. At the very least, it seems clear to me though that their isn't an obvious "value-add" from using cfinterface to warrant eliminating support for 7.0.2 within the next version of Illudium.
Now, one could argue why not just have a document somewhere that says "you must implement the following functions to be considered a valid plugin", but then you have to do all of the heavy lifting to introspect the CFC and make sure all the proper functions are there before you instantiate it. Why not just loosely type an interface and the requirements are then easily checked.
NOW, heres the issue with interfaces in a dynamic language (ie: CF), you can add functions at any time and the interface is only compile-time (I think...), so at runtime you can break the interface and nothing cares.
I don't remember if thats how it goes or not, I am sure someone more qualified than me would chime in if I am wrong.
So in short, depending on your application needs and how nice you want to treat your plugin/extension writers.
Your setDbms method doesn't have any arguments. If it did have an argument, you could set the type of the argument to IRdbms and any CFC that implemented that interface type could then be safely sent to the setDbms method.
In the future, as you add new specific types of Rdbms, those types would just need to implement your interface and the code in your setDbms method would not need to be changed. So cfinterface helps you take advantage of polymorphism.
For a more detailed explanation of what advantages I think CFinterface provides see:
http://www.brucephillips.name/blog/index.cfm/2007/8/5/ColdFusion-8-cfinterface-Example--Using-An-Interface-In-CF-8
Basically all the interface provides is a helper construct for other developers. The error and timing is different and subtly superior to just getting a method is undefined error. First, you'd get the error at object creation which helps avoid having an object with an improperly implemented interface getting created and then hanging around for a long time until later when an undefined method error occurs. And the error is more descriptive because it tells you about the interface and you'd easily be able to look to see what the interface specifies instead of guessing or relying on convention.
So, while I would argue that cfinterface is still useful, the usefulness is limited. Probably not worth eliminating CF7 users just to use them. But for internal projects that you know will be on CF8 they may be worth consideration.
I think interfaces create a brittle type system and pretty much the only situation where I'd use them is as "markers" (interfaces that declare no methods but simply provide a way to 'tag' CFCs as being of particular classes - hard to explain but if you Google 'marker interface' you'll get a lot of more coherent information!).
One issue I have with cfinterface is that it doesn't allow covariant return types or argument type specialization which I think are important concepts (again, go look them up if you care enough :) It also means that if you start using interfaces to specify types in your method arguments, I cannot pass you an object that implements some (or all) of its behavior through onMissingMethod() - so I can't pass an object that satisfies the behavior constraints but instead I am actually forced to write out methods long hand.
And your getDsn() interface has a returntype of void which should be string.
@Sean - you bring up another point I considered, being that at some point a specific implementation (say the cfdbinfo version) might choose to meet the requirements but differ slightly in the methods and this allows no variation. Also, thinks for catching the mistake, it's corrected.
@Jeff - BD and Railo are already not supported in that AFAIK they don't support the Flex integration that is necessary so that wasn't a consideration.
