Posts filed under 'Behavioral patterns'


Strategy Design Pattern in Coldfusion

In this blog post I will demonstrate the practical use of strategy design pattern in ColdFusion and discuss other possibilities.

So what is Strategy Design Pattern?
Wikipedia : “In computer programming, the strategy pattern (also known as the policy pattern) is a particular software design pattern, whereby algorithms can be selected at runtime.
Gang of four : “Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

So from the various descriptions it’s clear that this pattern can be used in the situation where there is possibility of having difference/multiple calculation and the application has to choose the right one on the fly.

I am going to use a scenario to build the case on why and how this pattern should be used.

Scenario:

Let’s say there is a web application which has multiple developers working on it, or have multiple teams working on independent section of the application, in such sort of application its fundamental to have core set of components that the developer use and there is also a possibility that they may be used across multiple applications. In such sort of scenario its inherent that core component be stable and least amount of changes should be done on the core components (i.e. cfc’s ) to avoid large scale impacts to other modules/developers.

So let’s say one of the core components that we have is the Customer.cfc it has some properties and also methods which get used by the appropriate section of the web applications


example1.jpg

<cfcomponent name="Customer" output="false">
  <cfscript>
    this.name = "";
    this.address = "";
    this.customerType = 0;
    this.balanceDue = 0;
  </cfscript>
</cfcomponent>
<!--- 
  ideally all the attributes should be in variables scope and have a get/set method defined, 
  but i am skipping that portion, so that i can focus on strategy pattern, and to have 
  less code :-) 
--->

There came a request for business to also start storing the latitude and longitude of customer location in the database and it was decided that we are going to use Google’s reverse lookup service, everybody agreed and the core Customer.cfc got updated to
example2.jpg

<cfcomponent name="Customer" output="false">
  <cfscript>
    this.name = "";
    this.address = "";
    this.customerType = 0;
    this.balanceDue = 0;
    this.latitude = 0;
    this.longitude = 0;
  </cfscript>
  
  <cffunction name="reverseLookup">
    <!--- 
      perform the reverse geocode with google  and return the values back as a string
      and also update the latitude and logitude attribute
    --->
    <cfreturn "">
  </cffunction>
</cfcomponent>

Here you can see two new properties were added and a method was added to perform the reverse lookup.

This worked fine, until business came back and said that they are not pleased with Google’s service and want to use yahoo, the simpler solution would be just remove the Google specific code and replace it with yahoo code, but the architect decided why not have both in place, who know the business may change their mind, so the code updated to.

example3.jpg

<cfcomponent name="Customer" output="false">
  <cfscript>
    this.name = "";
    this.address = "";
    this.customerType = 0;
    this.balanceDue = 0;
    this.latitude = 0;
    this.longitude = 0;
  </cfscript>
  
  <cffunction name="reverseLookup">
    <cfargument name="serviceID" type="numeric" required="true">
    <cfif serviceID EQ 1>
      <!--- google code--->
    <cfelseif serviceID EQ 2>
      <!--- yahoo code--->
    </cfif>
    <cfreturn "">
  </cffunction>
</cfcomponent>

As you can see the core component introduced the notion of serviceID, where the developers or consumer of this component has to supply the service ID i.e. yahooID or googleID. The change was made but it involved much pain because all the developers has to be notified about it and also the other applications that were using the component had to made updates because the interface for the core component got changed.

Now after some time, the business came up with another suggestion they had found this database that can be bought which has all the required data to perform the reverse lookup without going to any third party and wanted the development team to implement it. To accommodate this request the core component had to be again updated, with an additional IF condition
<cfcomponent name="Customer" output="false">
  <cfscript>
    this.name = "";
    this.address = "";
    this.customerType = 0;
    this.balanceDue = 0;
    this.latitude = 0;
    this.longitude = 0;
  </cfscript>
  
  <cffunction name="reverseLookup">
    <cfargument name="serviceID" type="numeric" required="true">
    <cfif serviceID EQ 1>
      <!--- google code--->
    <cfelseif serviceID EQ 2>
      <!--- yahoo code--->
    <cfelseif serviceID EQ 3>
      <!--- db code--->
    </cfif>
    <cfreturn "">
  </cffunction>
</cfcomponent>

As you can see in every situation the core component gets effected, i.e. any time a new vendor is found the core component has to be updated with additional IF logic, which means the core component has to be touched every time and developed team really does not want to do that because of downside impact that may happened if a bug got introduced during the change.

How does Strategy Pattern Solves the above problem?

Strategy Pattern is the perfect solution for the above mentioned scenario, where first of the the calculation/algorithm (i.e. the reverse geocoding) part of the code, gets split into a component of its own, so there will be a .cfc file for each of the reverse geocoding i.e. googleReversGeocoding.cfc , yahooReversGeocoding.cfc and databaseReversGeocoding.cfc and they all will inherit a single interface to perform decoding

example4.jpg
example5.jpg
IReverseLookup.cfc

<cfinterface name="IReverseLookup">
  <cffunction name="decode" returntype="string">
    <cfargument name="address" type="String" required="true">
  </cffunction>
</cfinterface>

GoogleReverseLookup.cfc

<cfcomponent name="GoogleReverseLookup" implements="IReverseLookup">
  <cffunction name="decode" returntype="String">
    <cfargument name="address" type="String" required="true">
    <!--- 
      make calls to google 
      and return the data
    --->
    <cfreturn "1929202.111, 292920.211">
  </cffunction>
</cfcomponent>

YahooReverseLookup.cfc

<cfcomponent name="YahooReverseLookup" implements="IReverseLookup">
  <cffunction name="decode" returntype="String">
    <cfargument name="address" type="String" required="true">
    <!--- 
      make calls to yahoo
      and return the data
    --->
    <cfreturn "0119182781.111, 5111192929.211">
  </cffunction>
</cfcomponent>

DatabaseReverseLookup.cfc

<cfcomponent name="DatabaseReverseLookup" implements="IReverseLookup">
  <cffunction name="decode" returntype="String">
    <cfargument name="address" type="String" required="true">
    <!--- 
      make calls to Database
      and return the data
    --->
    <cfreturn "000002221, 0028289.857">
  </cffunction>
</cfcomponent>

DatabaseReverseLookup.cfc

<cfcomponent name="Customer" output="false">
  <cfscript>
    this.name = "";
    this.address = "";
    this.customerType = 0;
    this.balanceDue = 0;
    this.latitude = 0;
    this.longitude = 0;
  </cfscript>
  
  <cffunction name="reverseLookup">
    <cfargument name="reversLookupStrategy" type="IReverseLookup" required="true">
    <cfreturn reversLookupStrategy.decode(this.address)>
  </cffunction>
</cfcomponent>

example1.cfm

<cfset customer = CreateObject("component", "Customer")>
<cfset customer.address = "410 washington Street, Greely, NC 10001">
<!--- set other properties --->

<!--- code wasts to use google service --->
<cfset strategy = CreateObject("component", "GoogleReverseLookup")>
<cfoutput>#customer.reverseLookup(strategy)#<br></cfoutput>

<!--- code wasts to use yahoo service --->
<cfset strategy = CreateObject("component", "YahooReverseLookup")>
<cfoutput>#customer.reverseLookup(strategy)#<br></cfoutput>

<!--- code wasts to use database service --->
<cfset strategy = CreateObject("component", "DatabaseReverseLookup")>
<cfoutput>#customer.reverseLookup(strategy)#<br></cfoutput>

So with the strategy pattern, now we will have cleaner implementation, where decoding with each vendor/service is done separately and Customer class (cfc) does not care about what vendor we are using as long the object that is being passed to it implements the IReverseLookup interface then that’s all it cares about. With this type of implementation you can add as my vendor and defined the logic in separate CFC (implement the interface) and you will never have to be updated the customer object. This way the code that is using this component can at runtime decide what strategy i.e. calculation to use without effecting the core component.

When can you use the strategy pattern be used?

a) If you have multiple components (CFC) that have structural similarity , but have different behavior (due diligence has to be done)
b) You have algorithm whose calculation can vary depending up the parameters passed to it.
c) Have excessive use of switch or if statement, and you have a desire to clear it up ?
d) You don’t want your core component to updated every time a new strategy is developed or requested.

Download Source Code

2 comments June 28th, 2008

observer design pattern in coldfusion

The observer design pattern is used in application development to observe the state of an object.  The best way to think about it is the publish / subscribe model.  In this pattern you have one object that is being observed (the subject), and a group of objects watching the subject called observers or listeners.  This pattern is an excellent example of loose coupling, because our classes can interact with very little knowledge of each other.

(read about the pattern @ http://en.wikipedia.org/wiki/Observer_pattern )

This post will demonstrate the usage of this pattern in ColdFusion

In the example I am creating a user list and the list if being watched by a logging observers that prints the message out to the browser anytime the new user gets added

IObservable.cfc
The Object that will provide notification to registered observers/listeners needs to implement this interface, in our case it will be UserList.cfc

  1. <cfinterface>
  2. <cffunction name="registerObserver">
  3. <cfargument name="observer" type="any">
  4. </cffunction>
  5.  
  6. <cffunction name="unregisterObserver">
  7. <cfargument name="observer" type="any">
  8. </cffunction>
  9.  
  10. <cffunction name="notifyObservers">
  11. <cfargument name="args" type="any">
  12. </cffunction>
  13. </cfinterface>

IObserver.cfc
The Object that will recieve notifications has to implement this interface, in our case it will be UserListLogger.cfc

  1. <cfinterface>
  2. <cffunction name="getInstanceID" returntype="String"/>
  3.  
  4. <cffunction name="onChanged">
  5. <cfargument name="sender" type="any">
  6. <cfargument name="args" type="any">
  7. </cffunction>
  8. </cfinterface>

UserList.cfc
This Class implements the IObservable interface

  1. <cfcomponent implements="IObservable">
  2. <cfscript>
  3. variables._observers = StructNew();
  4. </cfscript>
  5.  
  6. <cffunction name="addCustomer">
  7. <cfargument name="name">
  8. <!--- perform some action --->
  9. <cfset notifyObservers(arguments.name)>
  10. </cffunction>
  11.  
  12. <cffunction name="registerObserver">
  13. <cfargument name="observer" type="any">
  14. <cfif not StructKeyExists(variables._observers,arguments.observer.getInstanceID())>
  15. <cfset StructInsert(variables._observers, arguments.observer.getInstanceID(), arguments.observer)>
  16. </cfif>
  17.  
  18. </cffunction>
  19.  
  20. <cffunction name="unregisterObserver">
  21. <cfargument name="observer" type="any">
  22. <cfif StructKeyExists(variables._observers,arguments.observer.getInstanceID())>
  23. <cfset StructDelete(variables._observers, arguments.observer.getInstanceID())>
  24. </cfif>
  25. </cffunction>
  26.  
  27. <cffunction name="notifyObservers">
  28. <cfargument name="args">
  29. <cfloop collection="#variables._observers#" item="key">
  30. <cfset observer = StructFind(variables._observers, key)>
  31. <cfset observer.onChanged(this, arguments.args)>
  32. </cfloop>
  33. </cffunction>
  34. </cfcomponent>

UserListLogger.cfc
This Class implements the IObserver interface and at present only displays the information on browser

  1. <cfcomponent implements="IObserver">
  2. <cfset variables.instanceID = CreateUUID() >
  3.  
  4. <cffunction name="getInstanceID" returntype="String">
  5. <cfreturn variables.instanceID>
  6. </cffunction>
  7.  
  8. <cffunction name="onChanged">
  9. <cfargument name="sender" type="any">
  10. <cfargument name="args" type="any">
  11. <cfoutput><b>#arguments.args#</b> added to the user list<br></cfoutput>
  12. </cffunction>
  13. </cfcomponent>

test.cfm
The code here creates a UserList and registers the UserListLogger with it. The remaining part of the code adds user to the list and as soon the user is add, the UserListLogger gets notification of change.

  1. <cfset userList = CreateObject("component","UserList")>
  2. <cfset userList.registerObserver(CreateObject("component", "UserListLogger"))>
  3. <cfset userList.addCustomer( "Arjun" )>
  4. <cfset userList.addCustomer( "Vivaan" )>

The output of test.cfm will look like this


Arjun added to the user list
Vivaan added to the user list

Something worth watching is how the loose coupling of components, the UserList does not know what UserListLogger is doing or going to do with the notification. In fact there can be multiple listeners attached with UserList e.g. listeners to perform data base insert, listeners for email notification etc. The UserList does not care about how many or what type of listeners is attached to it, instead it just focuses on its task of notifying to the registered listeners about the change.
As you can see by following this pattern we can create ColdFusion applications that can adapt to changes and is easily maintainable.

Download Source Code

2 comments April 14th, 2008

Command Pattern in Coldfusion

Object in command pattern are used to represent action and can be used effectively in coldfusion, this Article explains the command pattern by building a demo application.

The tutorial is simple in its presentation but gets the job done and includes a full working application putting everything to good use. You’ll be creating a home automation Remote Control for the appliances in your house. My remote has simple on/off buttons and and undo button but the tutorial will teach you to do more

Read the full Article

Add comment December 10th, 2007

Implement the State pattern with ColdFusion Components

State Pattern implementation in coldfusion to maintain the various state of an object. The article provides step by step implementation along with downloadable code for future reference.

The introduction of ColdFusion Components (CFCs) in ColdFusion MX opened the door to more object-oriented programming in CF. When application developers start thinking about OOP, the idea of design patterns almost always comes up. In that context, Brian Kotek discusses the State pattern: its purpose, the problems it can solve, and how you can implement it using CFCs.

http://articles.techrepublic.com.com/5100-3513_11-6167539.html

Add comment December 4th, 2007

Strategy pattern Example for Coldfusion

Demo application with source code showing the usage of strategy pattern to dynamically select/change the class behavior at runtime and without having to lock the class to a specific behavior

As I make my way through Head First Design Patterns, which I am starting to agree is a “must” on a developer’s bookshelf, I decided I would work out some of the exercises in ColdFusion code. It is geared towards Java, but the principles carry over into OOP practices in ColdFusion as well.

Read the full Article

Add comment November 23rd, 2007


 Subscribe in a RSS reader


CourseLookup.us - Providing course and conference information to enhance your knowledge base.

Tags

Calendar

March 2010
M T W T F S S
« Dec    
1234567
891011121314
15161718192021
22232425262728
293031  

Posts by Month

Posts by Category