charlie griefer
just the perfect amount of cowbell
going OO (part 1)
Posted by: charlie griefer(if you're just joining us... you might want to read the "preface" to part 1)
so... getting right down to it, let's delve into the beginning of the first leg of the journey towards OO.
i wanted to create some sample code... and the concept of the 'employee' seems to be a fairly standard example in learning OO. i'm making a very conscious effort to take baby steps here, so i made this ridiculously simple (fetus steps, if you will). my database currently has one table named 'employee' with columns 'employeeID', 'employeeFirstName', and 'employeeLastName'.
now normally, i'd create one Employee.cfc (for those following along at home, it's good practice to capitalize CFC names, as it follows the convention of capitalizing class names in java). Employee.cfc would have all of the methods that relate to an employee. for example, getEmployeeByID(), getAllEmployees(), getEmployeesByDepartment(), etc. this, however, isn't quite... "right" in the world of OO.
it's been suggested that the "better" (more OO-ish) way would be to create three CFCs that relate to an employee:
- Employee_Bean.cfc
- Employee_DAO.cfc
- Employee_Gateway.cfc
here's the kicker. i don't know why (yet). realize that a lot of this journey is going to involve leaps of faith, and this is one of them. even though i believe that i understand the theory behind each of the above CFCs (i'll get into that below), i don't yet understand the advantages of having the three versus having a single CFC that models an employee. but i trust that before i get much further along, it'll become clear.
right then... so what is involved in each of the three employee CFCs?
a bean is a CFC that represents a single instance of a particular object (in this case, a single employee). i don't want to mire this down too much with the OO terminology... but the bean contains getters and setters (also referred to as accessors and mutators). any time i want to read a property of an employee (in this case my only choices are ID, firstname, and lastname), i would do so using a getter. for example, i'd have a method called getEmployeeID() or getEmployeeFirstName(). similarly, if i wanted to modify any of the properties, i'd use a setter method such as setEmployeeFirstName() or setEmployeeLastName(). bear in mind... these setters are setting the values for the object in memory... NOT for a record in the database. for that, we use a DAO. what's a DAO? glad you asked...
a DAO (data access object) is where the CRUD methods (create, read, update, delete) reside. the create method inserts a new record into the database. the read method retrieves a single record from the database. the update method updates a single record in the database, and the delete method... yeah, it deletes a single record from the database. each of these methods would receive an instance of the bean in memory as an argument in order to know which record to perform the requested operation on. the CRUD methods will only ever manipulate a single record. to interact with multiple records, you'd use a gateway.
a gateway is a CFC that allows your application access to the database. in my example code, my gateway currently has one method... getAllEmployees(). as you may have guessed, it simply retrieves all employee records from the database. as time goes on, i'm sure i'll be adding more (perhaps a getEmployeesByDepartment(), for example).
below is the code for all three CFCs. peruse if you'd like.
Employee_Bean.cfc:
<cfset variables.instance = structNew() />
<cffunction name="init" access="public" returntype="Employee_Bean" output="false">
<cfargument name="employeeID" type="string" required="false" default="" />
<cfargument name="employeeFirstName" type="string" required="false" default="" />
<cfargument name="employeeLastName" type="string" required="false" default="" />
<!--- run setters --->
<cfset setEmployeeID(arguments.employeeID) />
<cfset setEmployeeFirstName(arguments.employeeFirstName) />
<cfset setEmployeeLastName(arguments.employeeLastName) />
<cfreturn this />
</cffunction>
<cffunction name="validate" access="public" returntype="errorHandler" output="false">
</cffunction>
<!--- ACCESSORS/MUTATORS --->
<cffunction name="setEmployeeID" access="public" returntype="void" output="false">
<cfargument name="employeeID" type="string" required="true" />
<cfset variables.instance.employeeID = trim(arguments.employeeID) />
</cffunction>
<cffunction name="getEmployeeID" access="public" returntype="string" output="false">
<cfreturn variables.instance.employeeID />
</cffunction>
<cffunction name="setEmployeeFirstName" access="public" returntype="void" output="false">
<cfargument name="employeeFirstName" type="string" required="true" />
<cfset variables.instance.employeeFirstName = trim(arguments.employeeFirstName) />
</cffunction>
<cffunction name="getEmployeeFirstName" access="public" returntype="string" output="false">
<cfreturn variables.instance.employeeFirstName />
</cffunction>
<cffunction name="setEmployeeLastName" access="public" returntype="void" output="false">
<cfargument name="employeeLastName" type="string" required="true" />
<cfset variables.instance.employeeLastName = trim(arguments.employeeLastName) />
</cffunction>
<cffunction name="getEmployeeLastName" access="public" returntype="string" output="false">
<cfreturn variables.instance.employeeLastName />
</cffunction>
<cffunction name="getMemento" returntype="struct" hint="I contain the contents bro!">
<cfreturn variables.instance />
</cffunction>
<!--- DUMP --->
<cffunction name="dump" access="public" output="true" return="void">
<cfargument name="abort" type="boolean" default="false" />
<cfdump var="#variables.instance#" />
<cfif arguments.abort>
<cfabort />
</cfif>
</cffunction>
</cfcomponent>
Employee_DAO.cfc:
<cffunction name="init" returntype="Employee_DAO" output="false">
<cfargument name="dsn" type="string" required="true" />
<cfset variables.dsn = arguments.dsn />
<cfreturn this />
</cffunction>
<!--- CREATE --->
<cffunction name="insertEmployee" returntype="void" output="false">
<cfargument name="employee" type="Employee_Bean" required="true" />
<cfset var qInsertEmployee = "" />
<cfset arguments.employee.setEmployeeID(createUUID()) />
<cfquery name="qInsertEmployee" datasource="#variables.dsn#">
INSERT INTO employee (
employeeID,
employeeLastName,
employeeFirstName
) VALUES (
<cfqueryparam value="#arguments.employee.getEmployeeID()#" cfsqltype="cf_sql_char" />,
<cfqueryparam value="#arguments.employee.getEmployeeLastName()#" cfsqltype="cf_sql_char" />,
<cfqueryparam value="#arguments.employee.getEmployeeFirstName()#" cfsqltype="cf_sql_char" />
)
</cfquery>
</cffunction>
<!--- READ --->
<cffunction name="getEmployee" returntype="void" output="false">
<cfargument name="employee" type="any" required="true" />
<cfset var qGetEmployee = "" />
<cfquery name="qGetEmployee" datasource="#variables.dsn#">
SELECT
employeeID,
employeeLastName,
employeeFirstName
FROM
employee
WHERE
employeeID = <cfqueryparam value="#arguments.employee.getEmployeeID()#" cfsqltype="cf_sql_char" />
</cfquery>
<cfif qGetEmployee.recordcount GT 0>
<cfset employee.setEmployeeID(qGetEmployee.employeeID) />
<cfset employee.setEmployeeLastName(qGetEmployee.employeeLastName) />
<cfset employee.setEmployeeFirstName(qGetEmployee.employeeFirstName) />
</cfif>
</cffunction>
<!--- UPDATE --->
<cffunction name="updateEmployee" returntype="void" output="false">
<cfargument name="employee" type="Employee_Bean" required="true" />
<cfset var qUpdateEmployee = "" />
<cfquery name="qUpdateEmployee" datasource="#variables.dsn#">
UPDATE
employee
SET
employeeLastName = <cfqueryparam value="#arguments.employee.getEmployeeLastName()#" cfsqltype="cf_sql_char" />,
employeeFirstName = <cfqueryparam value="#arguments.employee.getEmployeeFirstName()#" cfsqltype="cf_sql_char" />
WHERE
employeeID = <cfqueryparam value="#arguments.employee.getEmployeeID()#" cfsqltype="cf_sql_char" />
</cfquery>
</cffunction>
<!--- DELETE --->
<cffunction name="deleteEmployee" returntype="void" output="false">
<cfargument name="employee" type="Employee_Bean" required="true" />
<cfset var qDeleteEmployee = "" />
<cfquery name="qDeleteEmployee" datasource="#variables.dsn#">
DELETE FROM
employee
WHERE
employeeID = <cfqueryparam value="#arguments.employee.getEmployeeID()#" cfsqltype="cf_sql_char" />
</cfquery>
</cffunction>
<!--- SAVE --->
<cffunction name="save" returntype="void" output="true">
<cfargument name="employee" type="Employee_Bean" required="true" />
<cfif isValid('UUID', arguments.employee.getEmployeeID())>
<cfset updateEmployee(arguments.employee) />
<cfelse>
<cfset insertEmployee(arguments.employee) />
</cfif>
</cffunction>
</cfcomponent>
Employee_Gateway.cfc:
<cffunction name="init" returntype="Employee_Gateway" output="false">
<cfargument name="dsn" type="string" required="true" />
<cfset variables.dsn = arguments.dsn />
<cfreturn this />
</cffunction>
<cffunction name="getAllEmployees" returntype="query" output="false">
<cfset var qGetAllEmployees = "" />
<cfquery name="qGetAllEmployees" datasource="#variables.dsn#">
SELECT
employeeID,
employeeLastName,
employeeFirstName
FROM
employee
</cfquery>
<cfreturn qGetAllEmployees />
</cffunction>
</cfcomponent>
i'll post entries that examine each of the three more closely, starting with the bean. in the meantime, if you're following along to learn, please feel free to post questions in the comments area and i'll do my best to answer (or to find an answer). if you're following along as a guide, please feel free to point out anything that i may have stated incorrectly, or to elaborate on anything that i may not have given the attention it deserved. your participation is greatly appreciated :)
for now, i'm going to try and get thru a few chapters of the object oriented thought process. this book comes highly recommended by one of my OO mentors scott stroz, who i'm sure has been driven to drink on more than one occasion after spending his time trying to help me out. i love you man! :)
charlie griefer




