Wednesday, 9 May 2012

Implementing polymorphism using the Strategy Design Pattern in PHP

The Strategy pattern is used to separate an object from its algorithms. The core idea of the pattern is the OOP principal of polymorphism. More explicitly, a Strategy pattern is a group of algorithms encapsulated inside classes that are made interchangeable so the algorithm can vary depending on the class used. Strategies are useful when you would like to decide at run time when to implement each algorithm. They can be used to vary each algorithm’s usage by its context.
The Strategy pattern has three main components: the Strategy, the Concrete Strategy, and the Context. The strategy is a common interface that ties together all supported algorithms. Usually this class is abstract. The concrete strategy is the implementation class where the logic for each different algorithm lives in the group. The context is the object that ties a concrete strategy to a particular code flow.


Advantage of the strategy pattern
As mentioned earlier, the motive behind the strategy pattern is to separate an object from its algorithms. Removing the algorithms from the host class and putting them in a separate class, enables the host to concentrate on its own job. Otherwise, the host will get messy with all the algorithms in itself with lot of if-else or switch-case statements.
Here’s an example.
Suppose a car renting service charges its customers differently at different situations – sometimes based on kilometers travelled, sometimes on hours of travel, and sometimes on a combination of both, depending on festive seasons, off-seasons etc.
There’s a billing class in the application which prepares the bill for the customer. Now the payable amount part needs a lot of calculations based on the type of charge, distance and hours of travel resulting in a lot of if-else or case statements. Addition of more billing algorithms results in inclusion of more if-else statements. To make the situation better, we simply encapsulate each faring algorithm in a separate class and depending on the context at runtime, we decide which algorithm to choose. Addition of another faring algorithm is done by simply adding another class i.e. extending the abstract class FareStrategy.

<?php
abstract class FareStrategy
{
    abstract function calculateFare($kms, $hrs);
}

class HourlyFareStrategy extends FareStrategy
{
    private $normalHours=8;
    private $normalHourRate=250;
    private $additionalHourRate=200;
   
    function calculateFare($kms, $hrs)
    {
        if($hrs > $this->normalHours)
        {
            $fare=($this->normalHours*$this->normalHourRate)+(($hrs-$this->normalHours)*$this->additionalHourRate);
            return $fare;
        }
        else
        {
            $fare=$hrs*$this->normalHourRate;
            return $fare;
        }       
    }
} //end class HourlyFareStrategy

class KmsFareStrategy extends FareStrategy
{
    private $normalKms=100;
    private $normalKmRate=15;
    private $additionalKmRate=13;
   
    function calculateFare($kms, $hrs)
    {
        if($kms>$this->normalKms)
        {
            $fare=($this->normalKms*$this->normalKmRate)+(($kms-$this->normalKms)*$this->additionalKmRate);
            return $fare;
        }
        else
        {
            $fare=$kms*$this->normalKmRate;
            return $fare;
        }
    }
}//end class KmsFareStrategy

class FaringContext
{
    private $_fareStrategy;
   
    public function __construct(FareStrategy $fareStrategy)
    {
        $this->_fareStrategy=$fareStrategy;
    }
   
    public function getFare($kms,$hrs)
    {
        return $this->_fareStrategy->calculateFare($kms,$hrs);
    }
} //end class FaringContext

//main program

$context=new FaringContext(new HourlyFareStrategy());
$fare=$context->getFare(120,6);
echo $fare;

echo '<br />';

$context=new FaringContext(new KmsFareStrategy());
$fare=$context->getFare(120,6);
echo $fare;

?>

document.write() overwrites the entire page

document.write is a function which basically outputs whatever is given to it. But sometimes, improper usage may overwrite the entire page d...