views:

266

answers:

1

I have two almost identical Beans.

Bean "Item" contains a unique identifier (primary key), a name and an array that contains structs with data for different Users that are related to the "Item".

Bean "User" contains a unique identifier (primary key), a firstname, a lastname and an array that contains structs with data of different Items that are related to the "User".

Both components contain two methods that write new data to the structs in the array or delete array elements and the setters and getters for the arguments.

The components deviate from each other only through theire argument names, the number of arguments each init method takes and the number of getters and setters.

So i want to create an "Abstract" Bean from which "Item" and "User" inherit. I can do so by using extends in the Beans to get the abstract methods, but how do i override the argument names and variable names. I.e. the argument name ArrayOfData in the AbstractBean does not really tell someone anything. I would like to have ItemHasMaterialsArray and UserHasItemsArray as names in the Item and User beans.

AbstractBean

<cfcomponent name="AbstractBean" output="false">
 <cffunction name="init" access="public" hint="constructor" returntype="AbtractBean">
  <cfargument name="Identifier" required="false" type="Numeric" default="0">
  <cfargument name="Name" required="false" type="String" default="">
  <cfargument name="ArrayOfData" required="false" type="Array" default="#arrayNew(1)#">

  <cfset variables.instance = structNew() />
  <cfset setIdentifier(arguments.identifier) />
  <cfset setName(arguments.name) />
  <cfset setArrayOfData(arguments.ArrayOfData) />
  <cfreturn this />
 </cffunction>

 <cffunction name="updateArrayOfData" access="public" output="true" returntype="boolean">
  <cfargument name="blank" type="Struct" required="true">
  <cfset var i = 0 />
  <cfset var form = arguments.blank />
  <cfset var ArrayOfData = arrayNew(1) />
  <cfset ArrayOfData = this.getArrayOfData() />

  <cfloop index="i" from="#arrayLen(ArrayOfData)#" to="1" step="-1">
   <cfif ArrayOfData[i].Amount neq structFind(form,ArrayOfData[i].Id) and LsIsNumeric(structFind(form,ArrayOfData[i].Id))>
    <cfset ArrayOfData[i].Amount = structFind(form,ArrayOfData[i].Id) />
   <cfelse>
    <cfset arrayDeleteAt(ArrayOfData,i) />
   </cfif>
  </cfloop>
  <cfset setArrayOfData(ArrayOfData) />
  <cfreturn true />
 </cffunction>

 <cffunction name="deleteArrayOfDataElement" access="public" output="false" returntype="boolean">
  <cfargument name="blank" type="Struct" required="true">
  <cfset var i = 0 />
  <cfset var form = arguments.blank />
  <cfset var ArrayOfData = arrayNew(1) />
  <cfset ArrayOfData = this.getArrayOfData() />

  <cfloop index="i" from="#arrayLen(ArrayOfData)#" to="1" step="-1">
   <cfif not listFind(form.del,ArrayOfData[i].UserId,',')>
    <cfset arrayDeleteAt(ArrayOfData,i) />
   </cfif>
  </cfloop>
  <cfset setArrayOfData(ArrayOfData) />
  <cfreturn true />
 </cffunction>

 <cffunction name="setIdentifier" hint="setter" access="private" output="false" returntype="Void">
  <cfargument name="identifier" required="true" type="Numeric" hint="Primary Key, really" />
  <cfset variables.instance.Identifier = arguments.Identifier />
 </cffunction>

 <cffunction name="getIdentifier" hint="getter" access="public" output="false" returntype="Numeric">
  <cfreturn variables.instance.identifier />
 </cffunction>

 <cffunction name="setName" access="private" output="false" returntype="Void">
  <cfargument name="name" required="true" type="String" />
  <cfset variables.instance.Name = arguments.Name />
 </cffunction>

 <cffunction name="getName" access="public" output="false" returntype="String">
  <cfreturn variables.instance.Name />
 </cffunction>

 <cffunction name="setArrayOfData" access="private" output="false" returntype="Void">
  <cfargument name="ArrayOfData" required="true" type="Array" />
  <cfset variables.instance.ArrayOfData = arguments.ArrayOfData />
 </cffunction>

 <cffunction name="getArrayOfData" access="public" output="false" returntype="Array">
  <cfreturn variables.instance.ArrayOfData />
 </cffunction>
</cfcomponent>

In my User bean i would like the init method to also take firstname as an argument and override the variable names in my methods. With telling names my other code, i.e. calling the getters, would be much cleaner. User.getUserHasMaterials() reads much better than User.getArrayOfData(), imo. is this even possible? If not than i guess i will have to sacrifice variables readability to avoid duplication.

+4  A: 

I think if you're trying to avoid duplication, it might make more sense to use composition rather than inheritance.

It looks like ArrayOfData could be be its own CFC, with methods like update(), delete(), and toArray(). Then you could pass an instance of ArrayOfData to either your User or Item object.

Your User CFC might look something like this:

<cfcomponent name="User">
 <cffunction name="init" access="public" hint="constructor" returntype="User">
   ...
   <cfargument 
      name="materials" 
       required="false" 
       type="ArrayOfData"
       default="#arrayNew(1)#"
       >
   ...

   <cfset variables.materials = createObject("component", "ArrayOfData")>
   <cfset setMaterials(arguments.materials)/>
   <cfreturn this />
 </cffunction>

 <cffunction name="setMaterials">
    <cfargument name="value" type="Array">
    <cfset variables.materials.update(arguments.value)>
 </cffunction>

 <cffunction name="getMaterials">
    <cfreturn variables.materials.toArray()>
 </cffunction>

 <cffunction name="deleteMaterials">
    <cfargument name="blank">
    <cfset variables.materials.delete(arguments.blank)>
 </cffunction>

Patrick McElhaney
thank you. you are right it is a "has a" relationship instead of "is a". I am getting a hang on all these oop concepts slowly. composition: checked ;)
mrt181