It is probably bad form to cover two topics in one blog post, but they came in together so I thought I'd answer them together. Here is what my reader asked:
Can you explain when you want to use the "extend" attribute in a CFC?My second question is, when would you use "init()"
The extend attribute tells ColdFusion that the CFC extends, or inherits, another CFC. A classic example of this would be to consider a Dog CFC that extends an Animal CFC. When a CFC extends another, the "child" gets all the methods of the "parent", so this can really help out with organization. If you were working on a Dog and Cat CFC and found that they shared multiple properties and methods, it would make sense to place that shared code into an Animal CFC that they could then extend.
One thing you don't want to use extends for is "utility" type functions. So for example, you may have a CFC that serves as a simple collection of helper functions. While your Dog CFC may need some of the methods, a Dog really isn't a Utility, so it doesn't make sense to extend it. The rule is, and this is not from me, but what I've read elsewhere, is that inheritance should only be used when the relationship between the two follows the "Is A" rule. So for example, a Dog "Is A" Animal, but not a Utility. For those cases, you simply want to create an instance of the other CFC inside the other CFC. How would you do that? That leads to our next question.
CFCs do not have a formal constructor, which is a method run by default when a CFC is created. ColdFusion will execute any code in a CFC that is not inside a method, but if you want a method to run on CFC creation, you need to do it yourself. Most ColdFusion developers are using an init method for this purpose. They create their CFC and call init on it immediately, normally using a format like so:
<cfset myCFC = createObject("component", "foo").init(somevar)>
You do not have to do this, but again, it is the recommended way to create and initialize a CFC. (Typically you will pass in things like a datasource or some other variable the CFC needs to operate.)
This is only a high level answer to your question. If you will be attending CFUNITED, I have a three hour session on CFCs and I'll be covering topics like these.
Archived Comments
Also important to keep in mind, for the variable "myCFC" to actually contain a reference to the CFC create, it the init() method needs to return a reference to the object:
<cfreturn this />
Hi Ray,
Great post as always! A couple of comments. One is that while you shouldn't extend general utilities, often there are utility services that use inheritance.
For instance, it makes perfect sense for a UserDAO to extend a BaseDAO. The base DAO is a collection of utilities, but they are specific to the purposes of a data access object. Same with a UserService extending BaseService - it is a great way to cut down on the amount of repetitive code you have as what is the difference between (say) deleting a user and deleting a page? Typically you can just parameterize the table name and ID field name and use the base class so you don't have to clutter up all of your entity classes with almost identical delete methods.
Also, one other thing that must be mentioned whenever inheritance comes up is the phrase: "favor composition over inheritance". It's a little much to cover in a comment, so it might be a good topic for a future Ask a Jedi once you get settled after your trip!
(For anyone who's wondering, just Google it - there's plenty of great info on the topic).
Best Wishes,
Peter
I'm going to be picky and say that FooDAO that extends BaseDAO is not a "utility" example, since FooDAO would pass a "Is A" BaseDAO test. That is different then say a Utility CFC that may have a Throw() method in it to allow CFCs to do stuff like,
<cfset throw("....")>
or may have a copy of ParagraphFormat2 from CFLib in it.
Hi Ray,
But there is no reason why classes containing utilities could not be extended. Would it be so unreasonable to call those "utility classes"?!
Lets say you have a BaseUtilities class in a shared directory which is inherited by BillsAutosUtilities allowing you to share utilities across projects. Or imagine a MedicalParser and an EngineeringParser class both extending a BaseParser which includes the ParagraphFormat2() method.
If I was being picky (*grin*) I'd argue that the set of utility classes and the set of classes passing an "is a" test may intersect (unless you DEFINE a utility class as one where the IS A relationship does not apply which I'd consider a slightly non standard definition).
However, getting away from the semantics, I agree 100% that "IS A" is a great test to use. It is not so much utility versus other classes as whether the set of methods (which presumable provide utility to the calling methods!) are grouped around a single object style concept which could be extended by another object of that same type or whether they are more like a general function library.
In fact, now that I think about it, I'm having a hard time coming up with a good OO example of a "simple collection of helper functions". Obviously you can put any group of functions into a cfc and call them methods, but the whole point in OOP is to try to group the methods along with associated data into coherent objects that could be subclassed.
The more I think about it, the more the "utility" or "helper" bit seems like a Red Herring. I'd just use the "IS A" test
Of course, even that is only a starting point - often you have an object with multiple IS A relationships and often it is better to use composition to solve those problems - it's like the old teacher/student problem. If you define teacher and student as children of person you're fine right up until the point that a student starts to teach a couple of classes in their spare time and then suddenly you are unable to either pay or grade them!
Best Wishes,
Peter
At last month's DFW CFUG meeting, Dave Shuck gave a presentation on "Subclassing DAOs for Flexible Apps", which covers how objects inherit properties from each other. Sample code posted with the presentation here:
http://www.daveshuck.com/in...
I thought it was very well done.
I have a problem with having to initialize myDataSourceName everytime I want to reference a table.
This is how I do it now:
foo.cfm contains:
<cfset FooObj = CreateObject("Component","Components.Foo").Init("myDataSourceName")>
<cfset FooQry = FooObj.View1()>
<cfdump var="#FooQry#">
foo.cfc contains:
<cfcomponent displayname="foo" output="False">
<cffunction name="Init" output="true" returntype="components.foo">
<cfargument name="DSN" required="yes">
<cfset Variables.DSN = arguments.DSN>
<cfreturn this>
</cffunction>
<cffunction name="View1" output="False" returntype="query" hint="I return everything from foo">
<cfset var rst = "">
<cfquery datasource="#Variables.DSN#" name="rst">
SELECT * FROM foo
</cfquery>
<cfreturn rst>
</cffunction>
</cfcomponent>
So here's my question:
Can you use extends to say that foo.cfc "is a" database table object, and should therefore
inherit the datasource name to use?
Simon Horwith wrote an article called "component.cfc: The Mother of All Components"
in the November 2003 issue of ColdFusion Developer's Journal (http://cfdj.sys-con.com/rea....
I wonder if you could put your datasource name in component.cfc and therefore not have to initialize it every time.
What I normally do is have a CFC called settings.cfc. It will either have some hard coded CFSETs, or read from an XML file. This will have my settings. The CFC will have something like getSettings() which returns a simple struct, and I'll pass that to my CFCs. So they get passed all the setting info for the app.
Hi Phillip,
A few comments:
Firstly, there is no "answer", just approaches that fit different situations better.
Right off, I'd recommend initializing your singletons like services, gateways and daos in application scope to improve performance.
Secondly, I'd encapsaulate that with a lazy loading factory so if you want to shift between request and application scope (test mode or production mode), you only have to change the "scope" variable in one place and always just call uyour factory to get the DAO with all of the scoping abstracted - also allows for abstract factory to easily switch between DAOs if required.
Thirdly, you could encapsulate the parameterizations for initialization within the factory, so you'd only have to have the DSN in one place anyway.
Finally, I keep promising to play with it, but I believe if you use ColdSpring you'll get all of the above benefits plus a few more, you won't have to code your factory, the parameters will be in XML, not code, and mortals shall swoon when they see your beautiful code :->
That said, yes, you could also have a base DAO that the entity DAO's extend. I'm playing with that now, with a bunch of the db calls parameterized and sitting within the base DAO (really, in a simple system, how different is delete from users and delete from articles?!). However, I would think twice about doing that JUST to pass in the DSN. You are creating a dependency between your base DAO and entity DAOs AND you are stopping the DAO being able to inherit from anything else (unless your base DAO also inherits from it).
Best Wishes,
Peter