Applying Strategy Pattern In C Applicationsby T. Kulathu Sarma When It Is Possible To Have SEVERAL DIFFERENT Algorithms for Performing a Process Strategy Pattern Can Be Used To Determine The Best Solution.
Introduction
Software consulting companies do projects for their customers on a "Fixed Price basis" or on a "Time and Material basis". Also, the projects can be either onsite or offsite. Usually, the customers specify how they want the project to be done ( Fixed price or Time and Material basis, onsite or offsite). The ultimate aim of the consulting company is to complete the project in the scheduled time, however the Strategy (or the policy) they adapt in doing the project may differ, depending on how This Is A Real Life Example, WHERE A Strategy Pattern is Applied.
Strategy Pattern can also be used in the software design. When it is possible to have several different algorithms for performing a process, each of which is the best solution depending on the situation, then a Strategy Pattern can be used. This article is all about Strategy Pattern. It uses a programming example to explain what, when and why a Strategy Pattern is needed. Benefits and drawbacks of using Strategy Pattern in software design is discussed. Three different approaches for implementing the Pattern in C and known uses of Strategy Pattern are Also Presented in this article.
Design Patterns are meant to provide a common vocabulary for communicating design principles Strategy Pattern is classified under Behavioral Patterns in the book, Design Patterns:... Elements of Reusable Object-Oriented Software by Erich Gamma et al (Addison-Wesley, 1995) In This Article, I Will Be Using The Terms Used by 'Gang of Four (GOF)' To Explain Strategy Pattern.an Example
A progress indicator is a window that an application can use to indicate the progress of a lengthy operation (for example, an Installation Process). It is usually a rectangular window that is gradually filled, from left to right, with the highlight color as the operation progresses. It has a range and a current position. The range represents the entire duration of the operation, and the current position represents the progress the application has made towards completing the operation. The range and the current position are used to determine the percentage Of The Progress INDICATOR To Fill with The Highlight Color.
Even though left to right direction is commonly used for filling in most progress indicators, other directions like right to left, top to bottom and bottom to top can also be used for filling. I have seen some progress indicators using a bottom to top filling. Also, Different Types of Fills Like Continuous Fill, Broken Fill Or Pattern Based Fills Can Be Used with a given flpring direction.
In short, the purpose of the progress indicator remains unchanged;. However, the filling direction or the filling algorithm can change Therefore, the family of algorithms used for filling can be encapsulated in a separate filler class hierarchy and the application can configure the progress indicator with a concrete filler class. An algorithm that is encapsulated in this way is called a Strategy. So, what is a Strategy pattern? The Strategy pattern is a design pattern to encapsulate the variants (algorithms) and swap them strategically to alter system behavior without changing its architecture. According to GoF, Strategy Pattern is intended to, Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.Strategy Pattern has three participants that include Strategy, Concrete Strategy and Context. In this Example, The Abstract Filler Class Cfiller, IS Referred As The Strategy, The Concrete Fil ler classes CLToRFiller (for providing Left to Right fill) and CRToLFiller (for providing Right to Left fill) are referred as Concrete Strategies and the progress indicator CProgressIndicator, is referred as the Context using Strategy. The application using the progress indicator is the client for The Context. Depending on The Situation, The Client Specifier The Progress Indicator (Context) with a concrete fliler class object (contrete strategy).
. CProgressIndicator maintains a reference to the CFiller object Whenever there is a progress in the operation, the application notifies CProgressIndicator (by calling a method like SetPos); the CProgressIndicator forwards the request to the CFiller object to visually indicate the change CFiller subclasses, CLToRFiller. and CRToLFiller implement the filling algorithm (in doFill method). By isolating the filling algorithm from the progress indicator, new filling strategies can be used without changing the progress indicator. Encapsulating the filling algorithm separately eliminates the need for multiple conditional statements to choose the right Strategy for Filling. Uml Diagram Showing The Relationship Between The Participants of The Strategy Pattern Is Presented Below.Approaches for Implementing Strategy Pattern In C
Push and Pull methods can be used to provide a means of safe data exchange and reduce the coupling between the Context and Strategy objects. In the Push method, the Context pushes the data to the Strategy object and the Strategy uses the data to implement the algorithm . in this method, Context might pass some unwanted data to the Strategy object, as all Concrete Strategy objects might not require all the data. On the other hand, in the Pull method, the Context, registers itself with the Strategy which in-turn maintains a reference to the Context object and pulls the required data from it. In this method, the Context must define an elaborate set of Get methods for the Strategy objects to pull the required data. Since, the Strategy maintains a reference to the Context, Both The Classes Are Tightly Coupled. The Choice of Push or Pull Method Purely Depends on The Requirement.
This article discusses three different approaches for implementing the Strategy Pattern in C . The approaches described below can use either a Push or Pull method.Strategy object as a required parameter to the ContextStrategy object as an optional parameter to the ContextStrategy as a template class parameter to The context
Strategy Object As a Required Parameter To The Context
In this approach, the progress indicator (Context) takes a filler (Strategy) object as a parameter in its constructor and maintains a reference to it. The progress indicator delegates the request to the filler object when SetPos method is called. Listing 1 shows this Approach. Also, Layout Manager in Java Uses this approach, see java and strategy pattern for explanation.
Advantage
................ ..
Application CAN SELECT The Required Filler Class Object At Run-Time. SetFiller Method Can Be Used to Change The Filler Class ObjectAfter Creating The Progress Indicator.
Disadvantage
Application Using The Progress Indicator Must Be Aware of All The Filler Classes and Must Supply The Progress Indicator with The Required Filler Class Object.
ProGress Indicator Is Not Having Any Control on The Scope or The Lifetime of The Filler Class Object.
Strategy Object As An Optional Parameter To The Context
This approach is similar to the first approach, but the filler object (Strategy) is taken as an optional parameter when progress indicator (Context) is created. The progress indicator creates a default filler object (Left to Right filler), if the application did NOT SPECIFY THE FILLER Object During Construction. Listing 2 Approach. Demo Application Provided with this article Uses this technique.advantage
Application CAN Specify The Filler Object Only When It Needs to Change The Default Filler Object.
Application CAN SELECT The Required Filler Class Object At Run-Time. SetFiller Method Can Be Used to Change The Filler Class ObjectAfter Creating The Progress Indicator.
Disadvantage
Progress Indicator Must Be Aware of The Concrete Filler Class CLTORFILLER, For Providing The Default Behavior. This Increases The Coupling Between The CPROGRESSINDIATOR AND CLTORFILLER CLASSES.
Progress indicator has control only on the lifetime of the default filler object, which is CLToRFiller object in this case. But, it is not having any control on the scope or the lifetime of other filler class objects.
Strategy As a Template Class Parameter to the Context
If there is no need to change the filler class (Strategy) at run time, it can be passed as a template parameter to the progress indicator (Context) class at compile time. Listing 3 shows this approach. Active Template Library (ATL) uses A Variation of this approach to select The Required CComObjectxxx <> in which the context is passed as a parameter to the statulegy class (pull method). See atl and strategy pattern for expedition .. SEE ATHOD.
Advantage
Progress indicator template class is instantiated only with concrete filler classes, so there is no need for the abstract CFiller class.Passing filler class as a template parameter provides an early binding between the progress indicator and the filler classes. This avoids runtime overhead and increases the Efficiency.
Progress Indicator Is Responsible for the Creation of The Filler Class Object. Therefore, IT Has Full Control on The Lifetime of The Object.
Disadvantage
............
Benefits in Using Strategy Pattern
A Family Of Algorithms Can Be Defined As a class hierarchy and can be used interchangeably to alter Application Behavior without Changing ITS Architecture.
.
The Application CAN Switch Strategies At Run-Time.
Strategy Enables The Clients To Choose The Required Algorithm, WITHOUT USING A "Switch" statement or a series of "if-else" statements.
Data Structures Used for Implementing The Algorithm IS Completely Encapsulated In Strategy Classes. Therefore, The Implementation of an Algorithm Can Be Changed WITHOUT Affecting The Context Class.
STRATEGY PATTERN CAN BE Used INSTET OF SUB-CLASING The Context Class. Inheritance Hardwires The Behavior With the Context and The Behavior Cannot Be Changed Dynamical.
The Same Strategy Object Can Be Strategically Shared Between Different Context Objects. However, The Shared Strategy Object Should Not Maintain State Across Invocations.
DrawBacks in Using Strategy Pattern
The application must be aware of all the strategies to select the right one for the right situation.Strategy and Context classes may be tightly coupled. The Context must supply the relevant data to the Strategy for implementing the algorithm and sometimes, all the data passed by The Context May Not Be Relevant To All the Concrete Strategies.
Context and the Strategy classes normally communicate through the interface specified by the abstract Strategy base class. Strategy base class must expose interface for all the required behaviors, which some concrete Strategy classes might not implement.
IN MOST CASES, The Application Configes The Context with the Required Strategy Object. The Application Needs To Create and Maintain Two Objects in Place of ONE.
Since, the Strategy object is created by the application in most cases;.. The Context has no control on lifetime of the Strategy object However, the Context can make a local copy of the Strategy object But, this increases the memory requirement and has a Sure Performance Impact.
KNown Uses
.
ATL and Strategy Pattern
.............................. ..
In ATL, the class of the COM object is not instantiated directly. It acts as a base class for a CComObjectxxx <> class. For example, if CMyClass is the COM object class, then the most derived class in the class hierarchy will be a CComObjectxxx
Java and Strategy Pattern
Strategy Pattern is also used in the implementation of the Layout Manager in Java. The Layout manager can be configured with a layout object, which can be an object of a FlowLayout, a CardLayout, a GridLayout or a GridBagLayout class. These classes encapsulate the algorithms For Laying Out Visual Components and the Provide Several Different Layouts for Viewing The Same Visual Widgets.other Known Uses
Borland's ObjectWindows uses strategies to encapsulate validation algorithms for dialog box entry fields. For example, a numeric field might have a validator to check proper range, a date field might have a validator to check the correctness of the input date and string field might have a Validator for Proper Syntax.
ET Uses The Strategy Pattern to Encapsulate Layout Algorithms for Text Viewers.
Strategy Pattern Is Also Used In Many Popular Sorting Algorithms, Graph Layout Algorithms and Memory Allocation Algorithms.
Bridge and statygy
Often, the Strategy Pattern is confused with the Bridge Pattern. Even though, these two patterns are similar in structure, they are trying to solve two different design problems. Strategy is mainly concerned in encapsulating algorithms, whereas Bridge decouples the abstraction from the implementation, TO Provide Different IMPLEMENTATION for the Same Abstract.
Summary
This article is all about the Strategy Pattern, it not only talked about what Strategy Pattern is, but also emphasized why and when it is needed. I have used this pattern in many of my projects including the implementation of Lexical Analyzer and Parser classes. This pattern can be applied wherever there are several different ways of performing the same task. In short, the Strategy pattern can be used to encapsulate varying algorithms and use them to change the system behavior without altering its architecture.Acknowledgments
Spectial Thanks The My Friend Sree Meenakshi for Her Herpful Suggestions In Improving The Clarity and Presentation of this Article.
Listing 1 - Strategy Object As a Required Parameter To The Context
// Forward Declaration for CFiller Class
Class Cfiller;
// Class Declaration for CPROGRESSINDICATOR
Class CProgressindicator
{
//Method Declarations
PUBLIC:
CPROGRESSINDICATOR (CFILLER *);
Int setPos (int);
Int setfiller (cfiller *);
...
...
// Data MEMBERS
protected:
CFILLER * M_PFILLER;
}
// CProgressindicator - Implementation
CPROGRESSINDICATOR :: CProgressindicator (CFiller * Pfiller)
{
// Validate Pfiller
Assert (pfiller! = Null);
m_pfiller = pfiller;
}
Int CProgressindicator :: SetPOS (int NPOS)
{
// Some Initialization Code Before Forwarding The Request to FILLER Object
...
...
// Request Forwarding to FILLER OBJECT
Int nstatus = m_pfiller-> DOFILL (...);
...
...
Return nStatus;
}
INT * CPROGRESSINDICATOR :: setfiller (cfiller * pfiller)
{
// Validate Pfiller
Assert (pfiller! = Null);
// set new filler object
m_pfiller = pfiller;
Return 0;
}
Listing 2 - Strategy Object As an Optional Parameter To The Context
// Forward Declaration for CFiller Class
Class Cfiller; // Class Declaration for CPROGRESSINDICATOR
Class CProgressindicator
{
//Method Declarations
PUBLIC:
CPROGRESSINDICATOR (CFiller * = null);
Virtual ~ cprogressindicator ();
Int setPos (int);
Int setfiller (cfiller *);
...
...
// Data MEMBERS
protected:
CFILLER * M_PFILLER;
BOOL M_BCREATED;
}
// CProgressindicator - Implementation
CPROGRESSINDICATOR :: CProgressindicator (CFiller * Pfiller)
{
// Check and create Filler Object
IF (pfiller == null)
{
// Create a Default Left to Right Filler Object
m_pfiller = new cltorfiller;
m_bcreated = true;
}
Else
{
m_pfiller = pfiller;
m_bcreated = false;
}
}
CProgressindicator :: ~ cprogressindicator ()
{
// delete filler object, only if it is create by the program inde
IF (m_bcreated == True)
{
Delete m_pfiller;
}
}
Int CProgressindicator :: SetPOS (int NPOS)
{
// Some Initialization Code Before Forwarding The Request to CFiller Object
Assert (m_pfiller! = Null);
...
...
// Request Forwarding to CFILLER OBJECT
Int nstatus = m_pfiller-> DOFILL (...);
...
...
Return nStatus;
}
INT * CPROGRESSINDICATOR :: setfiller (cfiller * pfiller)
{
// Validate Filler Object
Assert (pfiller! = Null);
// delete filler object, only if it is create by the program inde
IF (m_bcreated == True)
{
Delete m_pfiller;
m_bcreated = false;
}
// set new filler object
m_pfiller = pfiller;
Return 0;
}
Listing 3 - Strategy As a Template Class Parameter
Template
{
//Method Declarations
PUBLIC:
Int setPos (int);
...
...
// Data MEMBERS
protected:
Tfiller M_Thefiller;
}
// CProgressindicator - Implementation
INT CPROGRESSINDICATOR :: setPos (int NPOS) {
// Some Initialization Code Before Forwarding The Request to CFiller Object
...
...
// Request Forwarding to CFILLER OBJECT
INT NSTATUS = m_thefiller.dofill (...);
...
...
Return nStatus;
}
// Application Code Using CPROGRESSINDICATOR
CPROGRESSINDICATOR