dimanche 26 avril 2015

Setting the strategy in a Strategy Pattern

I might have implementing this wrong as I cannot figure out a solid way of setting which strategy to use in my implementation of the Strategy Pattern. I'm not a big fan of writing it "statically", perhaps there is another way.

Backstory: I've done two (2) implementations (soap + http) to shipping providers in order to retrieve Track & Trace information for whatever the user inputs frontend. They each follow an interface so that I know which functions are and should (PHP :3) be available. I've shortened the class names below as this is Magento and class names are very long.

Flow: Customer inputs tracking number in a form and submits. Request is sent to the controller, controller initializes an instance of Service class, sets output via. $service->setOutput('tracking/service_gls') - note that tracking/service_gls just maps directly to the service class (Magento thing), $service->getDeliveryInformation($number) is called (we know this exists because of the interface), the entire $service object is returned to the view and data is presented.

My challenge: I'm using a switch case to set tracking/service_gls and tracking/service_otherservice then calling getDeliveryInformation(). Is this the correct approach? I feel it's a bit too static and hard to maintain if someone wants to connect another shipping provider. They would have to enter the controller and manually add another entry to the switch case.

Code has been shortened a bit as there are lots more functions, but not important.

Interface the two shipping provider models are implementing

interface Output
{
    /* Requests delivery information for the specified tracking number */
    public function getDeliveryInformation($number);

    /**
    * Returns acceptor name
    * @return string
    */
    public function getAcceptorName();
}

Service class handles requesting data from the shipping models

class Service
{
    protected $output;


    /**
     * Sets the output model to use
     * @param string $outputType
     */
    public function setOutput($outputModel)
    {
        // SO note: same as doing new Class()
        // Magento people note: getModel() works fine tho.. ;-)
        $modelInstance = Mage::app()->getConfig()->getModelInstance($outputModel);
        $this->output = $modelInstance;
    }

    /**
     * Returns delivery information for the specified tracking number
     * @param string $number
     * @return instance of output class
     */
    public function getDeliveryInformation($number)
    {
        // SO note: This makes the shipping class request
        // information and set data internally on the object
        $this->output->getDeliveryInformation($number);
        return $this->output;
    }
}

Example of a shipping class; I have two in this case

class Service_Gls implements Output
{
    const SERVICE_NAME = 'GLS';
    const SERVICE_URL = 'http://ift.tt/1KjcQzi';

    protected $locale = 'da_DK';


    /* Class constructor */
    public function __construct() { }

    /**
     * Requests delivery information for the specified tracking number
     * @param mixed $number
     */
    public function getDeliveryInformation($number)
    {
        $this->_getDeliveryInformation($number);
    }

    /**
     * Requests and sets information for the specified tracking number
     * @param mixed $number
     */
    private function _getDeliveryInformation($number)
    {
        // SO note: Extending from Varien_Object has magic __get, __set .. hence why there is no getData() function in this class.
        if (!count($this->getData()))
        {
            $client = new SoapClient($url);
            $client->GetTuDetail($reference));

            .. set data
        }
    }

    /**
     * Returns acceptor name
     * @return string
     */
    public function getAcceptorName()
    {
        $signature = $this->getSignature();
        return (isset($signature)) ? $this->getSignature() : false;
    }

    /**
     * Returns the name of the current service
     * @return string
     */
    public function __toString()
    {
        return self::SERVICE_NAME;
    }
}

Aucun commentaire:

Enregistrer un commentaire