By Peter Bell

The Benefits of Base Classes

"Favor composition over inheritance". It is one of the basic tenets of Object Oriented design. However, for green field developments, base classes can be a quick and easy way of getting a functional application up and running and a good way to make your code less repetitive and more maintainable.

Let’s take the classic example of a DAO. The purpose of a Data Access Object is to allow CRUD (Create, Read, Update and Delete) operations on a persistable entity (one that will be stored in and retrieved from a persistent storage mechanism – usually a relational database although it could be a file or a call to some “black box” web service that hides the persistence mechanism from your application).

Lets pretend for a moment that we have a really simple application where every persistent object is stored in a single table and where any relationship logic between objects (User has many addresses) for retrieval, editing and cascaded deleting purposes is taken care of elsewhere. What would our DAOs look like?

Well, there would probably be four core methods to handle insert, update, delete and get operations (later this week I'll blog on a slightly less naïve implementation including the ability to get by different parameters and a public save() method hiding private insert() and update() methods).

Let’s just take one of those methods – delete. The core query for a User object is going to look something like:

<cfquery name="DeleteUser" datasource="#VARIABLES.Datasource#">
   DELETE
   FROM UserTable
   WHERE UserID = <cfqueryparam value = "#ARGUMENTS.UserID#" cfsqltype = "cf_sql_integer">
</cfquery>

Now imagine the same for an Article:

<cfquery name="DeleteArticle" datasource="#VARIABLES.Datasource#">
   DELETE
   FROM ArticleTable
   WHERE ArticleID = <cfqueryparam value = "#ARGUMENTS.ArticleID#" cfsqltype = "cf_sql_integer">
</cfquery>

or a Product:

<cfquery name="DeleteProduct" datasource="#VARIABLES.Datasource#">
   DELETE
   FROM ProductTable
   WHERE ProductID = <cfqueryparam value = "#ARGUMENTS.ProductID#" cfsqltype = "cf_sql_integer">
</cfquery>

Seeing a pattern yet?! Now of course, you could generate this code pretty quickly using a CFTemplate and metadata, but why mess up your application with all of this redundant code? Imagine if you ever had to manually update the application to use “soft deletes” replacing the DELETE with an UPDATE and a bit called something like Deleted which would then be added as part of the WHERE clause to any get() methods for the objects. It would be a lot of redundant, error prone work.

So, why not create a BaseDAO? A BaseDAO can provide a default implementation for all of the basic methods, so all you need to do is to create a UserDAO.cfc that extends BaseDAO as follows:

<cfcomponent displayname="User DAO" hint="I am the Data Access Object for CRUD operations on a single User." extends="application_name.com.base.model.baseDAO" output="no">
<cffunction name="Init" returntype="userDAO" access="public" output="no" displayname="Initialize" hint="I initialize the User DAO.">
   <cfscript>
      // Call parents initialization script       super.init();
      // Add any User specific initialization here       VARIABLES.TableName = “UserTable”;
      VARIABLES.IDFieldName = “UserID”;
      VARIABLES.FieldNameList = “FirstName,LastName,UserID”;
   </cfscript>
   <cfreturn this />
</cffunction>
<!--- Add any custom methods here --->

<!--- END Add any custom methods here --->
</cfcomponent>

Now you need to make sure that you have a BaseDAO that performs all of the key operations required. For instance, the BaseDAO.delete() will include:

<cfquery name="Delete" datasource="#VARIABLES.Datasource#">
   DELETE
   FROM #VARIABLES.TableName#
   WHERE #VARIABLES.IDFieldName# = <cfqueryparam value = "#ARGUMENTS. ID#" cfsqltype = "cf_sql_integer">
</cfquery>

A real world example would also include a VARIABLES.IDFieldDataType property as the above code assumes the ID field for the entity is an integer which is not always the case.

That’s it. If you have a BaseDAO with CRUD functions, the above code will immediately give you the ability to create, read, update and delete records for your business entity, but if you need to overload the methods for a given object (perhaps User.delete() should also delete the users addresses) you can just add a delete method to the child class and that will be used instead of the parents method.

Inheritance is not a panacea. It creates a very tight dependence between objects within the hierarchy and because ColdFusion (luckily) does not support multiple inheritance, once you’ve chosen something to inherit you’re out of inheritance options. However, where there is a very clear "is a" relationship (a UserDAO clearly IS A DAO), base classes can be a great solution.

Firstly they allow you to get the application up and running in a simplistic way very quickly indeed. Secondly they can make it easier to avoid repetitive code (as the Pragmatics say: Don’t Repeat Yourself), creating a much more maintainable application.

Comments
Hi Peter, this is very clever. Is it possible to put an complete baseDao example here.
Thanks.
Will
# Posted By Will Song | 12/13/06 11:16 PM
Hi Will,

Thanks for the comment! I will be releasing a full set of base classes (BaseService, BaseDAO, BaseIBO, BaseController, BaseObject and maybe one or two others) as part of the LightBase framework so keep an eye out for that later this month - I'll blog about it when I release it!
# Posted By Peter Bell | 12/14/06 8:54 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.