I think Roland's method is definitely a move in the right direction.

In fact, I'd probably throw out inheritance altogether - what defines
a "car" or a "truck" and what capabilities each has is a shift
subject...some cars can tow, and some trucks can move pretty quickly. 
Before long, we may have Car, Truck, CarThatCanTow,
TruckThatCanGoFast, ConvertibleCar, ConvertibleTruck (blame
chevrolet), TwoDoorCar (not convertible!),  TwoDoorTruck,
ConvertibleTwoDoorCar, and...well...crap, that's a lot of maintenance.

I think one way to approach it would be to implement each behavior
(towing, going fast, being able to put the top down (which some wacky
chevy "trucks" can do), etc.) as an "abstract" class, and when a "car"
is created (probably by a factory), it just gets composed of the
proper concrete implementations of the behaviors.  I think some people
lump this together under the general monicker of the "Behavior"
pattern.

(I shortcut and assign the abstractTow and abstractSpeed behaviors
because, well, we can in CF, and I just have the abstract ones throw
errors if they get executed.)

It'd look something like this:

<cfcomponent name="car">
<cffunction name="init">
  <cfset variables.towBehavior = createObject("component",
"abstractTow").init()>
  <cfset variables.speedBehavior = createObject("component",
"abstractSpeed").init()>
</cffunction>

<!--- Getters and setters for towBehavior / speedBehavior --->

<!--- /Getters and setters for towBehavior / speedBehavior --->

<!--- Returns towing capacity - not necessarily a number, may be an object! --->
<cffunction name="doTowBehavior">
  <cfreturn variables.towBehavior.doSomething() />
</cffunction>

<!--- Returns information about speed  - not necessarily a number, may
be an object! --->
<cffunction name="doSpeedBehavior">
  <cfreturn variables.towBehavior.doSomething() />
</cffunction>

</cfcomponent>


...the implementation is way incomplete, and in reality, may be a lot
more complex, with the behaviors acting on / interacting with the
"mother" object, possibly through package-only methods, etc.  What's
important is that we're moving away from inheritence, which locks us
into a given set of behaviors at compile-time, and moving towards
runtime composition, where we can "build" objects that perform in a
given way at will.

It does, though, have consequences.  If suddenly we get a rash of cars
that have five wheels, we'll have to go add a new property to the Car.
 However, because we don't have a complicated inheritance tree, we
don't wind up impacting a gazillion other implementations of cars. 
Heck, maybe that's more of an advantage that a consequence.  I'm sure
someone else can point out a downside to this approach.

-Joe


On Apr 6, 2005 6:35 PM, Roland Collins <[EMAIL PROTECTED]> wrote:
> You wouldn't need super-class checking if you're using the built-in cf
> "extends" keyword because you're forcing the contract at compile time
> instead of at runtime.
> 
> Adding the Implements keyword into the component name is still every bit as
> programmatic as using roles because it is functionality that doesn't mean
> anything without the user-defined instanceOf function.  In fact, we're
> really just arguing sematics here - our versions do exactly the same thing.
> Both of our versions rely on a bunch of strings which are not enforced by
> the underlying language, and we're both just searching through a list of
> strings for the one that we're looking for.  You just declare yours in the
> function metadata, while I declare them in the init :)  It's really a matter
> of preference.
> 
> As far as code reuse is concerned, you only need to have the three methods
> (getRoles, addRole, and hasRole) in the base component, so it's only in one
> place.  There's no need to add it to each derived class.  And it's very
> simple code at that:
> 
> <cfcomponent displayname="Vehicle">
> 
> <cfset variables.Roles = StructNew()>
> 
> <cffunction name="getRoles" returntype="struct" hint="Retrieves the current
> component's roles." access="public">
>         <cfreturn StructCopy(variables.Roles)/>
> </cffunction>
> 
> <cffunction name="addRole" returntype="void" hint="Adds to current
> component's roles." access="package">
>         <cfargument name="roleName" type="string" required="true" hint="The
> name of the role to add."/>
>         <cfset StructInsert(variables.Roles, arguments.roleName, "", true)>
> </cffunction>
> 
> <cffunction name="hasRole" returntype="boolean" hint="Adds to current
> component's roles." access="public">
>         <cfargument name="roleName" type="string" required="true" hint="The
> name of the role to add."/>
>         <cfreturn StructKeyExists(variables.Roles, arguments.roleName)/>
> </cffunction>
> 
> </cfcomponent>
> 
> In the derived classes, it's even easier - all you need to do is add one
> line in the init or pseudo-constructor area.
> 
> <cfcomponent extends="Vehicle" displayname="Truck">
>         <cfset this.addRole("Truck")>
> </cfcomponent>
> 
> It's also noteworthy to add that the final end-user's calling code cannot
> modify the roles that the component has using this method, whereas it can
> using the GetMetaData approach.  Anyway, we're both accomplishing the same
> thing - it's really a matter of preference until CF finally adds interfaces
> :)
> 
> Cheers,
> Roland
> 
> -----Original Message-----
> From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On Behalf
> Of Barney Boisvert
> Sent: Wednesday, April 06, 2005 5:39 PM
> To: [email protected]
> Subject: Re: [CFCDev] Question on class / subclass
> 
> Overkill?  Because it's more flexible, or because it's less code?
> Here's a reimplemented instanceOf method (the only change is the new
> predicate on line 9).  Definitely simpler than adding three new
> methods to your classes, and then adding more code to your init method
> to use them.  Especially since you'll probably need the instanceOf
> method anyway for doing superclass checking.
> 
> <cffunction name="instanceOf" access="public" output="false"
> returntype="boolean"
>         hint="I return whether this object is an instance of the specified
> type">
>         <cfargument name="type" type="string" required="true" />
>         <cfset var md = getMetaData() />
>         <cfset var curr = md />
>         <cfif NOT structKeyExists(md, "instanceOf_" & type)>
>                 <cfset md["instanceOf_" & type] = false />
>                 <cfloop condition="true">
>                         <cfif curr.name EQ type OR ( structKeyExists(curr,
> "implements")
> AND listFindNoCase(curr.implements, type) GT 0 )>
>                                 <cfset md["instanceOf_" & type] = true />
>                                 <cfbreak />
>                         </cfif>
>                         <cfif NOT structKeyExists(curr, "extends")>
>                                 <cfbreak />
>                         </cfif>
>                         <cfset curr = curr.extends />
>                 </cfloop>
>         </cfif>
>         <cfreturn md["instanceOf_" & type] />
> </cffunction>
> 
> On Apr 6, 2005 2:13 PM, Roland Collins <[EMAIL PROTECTED]> wrote:
> > Right, but I think it's overkill in this case :)
> 
> --
> Barney Boisvert
> [EMAIL PROTECTED]
> 360.319.6145
> http://www.barneyb.com/
> 
> Got Gmail? I have 50 invites.
> 
> ----------------------------------------------------------
> You are subscribed to cfcdev. To unsubscribe, send an email to
> [email protected] with the words 'unsubscribe cfcdev' as the subject of the
> email.
> 
> CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting
> (www.cfxhosting.com).
> 
> An archive of the CFCDev list is available at
> www.mail-archive.com/[email protected]
> 
> ----------------------------------------------------------
> You are subscribed to cfcdev. To unsubscribe, send an email to 
> [email protected] with the words 'unsubscribe cfcdev' as the subject of the 
> email.
> 
> CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting 
> (www.cfxhosting.com).
> 
> An archive of the CFCDev list is available at
> www.mail-archive.com/[email protected]
> 
> 


-- 
Get Glued!
The Model-Glue ColdFusion Framework
http://www.model-glue.com


----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to 
[email protected] with the words 'unsubscribe cfcdev' as the subject of the 
email.

CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting 
(www.cfxhosting.com).

An archive of the CFCDev list is available at
www.mail-archive.com/[email protected]


Reply via email to