Php 在战略模式中设定战略

Php 在战略模式中设定战略,php,design-patterns,refactoring,Php,Design Patterns,Refactoring,我可能在实现这一点时出错,因为我无法找到一种可靠的方法来设置在实现策略模式时使用的策略。我不太喜欢“静态”写作,也许还有另一种方式 后台故事:为了检索用户在前端输入的任何内容的跟踪和跟踪信息,我已经对发货提供商进行了两(2)次实现(soap+http)。它们都遵循一个接口,这样我就知道哪些函数是可用的,哪些函数(PHP:3)应该是可用的。我缩短了下面的类名,因为这是Magento,类名很长 流程:客户在表单中输入跟踪编号并提交。请求被发送到控制器,控制器初始化服务类的实例,通过设置输出$serv

我可能在实现这一点时出错,因为我无法找到一种可靠的方法来设置在实现策略模式时使用的策略。我不太喜欢“静态”写作,也许还有另一种方式

后台故事:为了检索用户在前端输入的任何内容的跟踪和跟踪信息,我已经对发货提供商进行了两(2)次实现(soap+http)。它们都遵循一个接口,这样我就知道哪些函数是可用的,哪些函数(PHP:3)应该是可用的。我缩短了下面的类名,因为这是Magento,类名很长

流程:客户在表单中输入跟踪编号并提交。请求被发送到控制器,控制器初始化服务类的实例,通过设置输出$service->setOutput('tracking/service_gls')-请注意,tracking/service_gls只是直接映射到服务类(Magento thing),调用$service->getDeliveryInformation($number)(我们知道这是因为接口而存在),整个$service对象返回到视图并显示数据

我的挑战:我正在使用开关盒设置跟踪/服务\u gls和跟踪/服务\u otherservice,然后调用getDeliveryInformation()。这是正确的方法吗?我觉得如果有人想连接另一个运输提供商,它有点太静态,很难维护。他们必须进入控制器并手动向开关盒添加另一个条目,在类中200行深处的某个函数中

控制器外观示例:

public function getDeliveryInformationAction()
{
    $id = $this->getRequest()->getParam('id', false);
    if ($id && $this->getRequest()->isAjax())
    {
        // SO note: service parameter is just two radio buttons with values "gls", "otherservice"
        $serviceType = $this->getRequest()->getParam('service', false);

        try
        {
            // SO note: same as doing new Class()
            $service = Mage::getModel('tracking/service');

            switch ($serviceType)
            {
                case 'gls':
                $service->setOutput('tracking/service_gls');
                break;

                case 'other':
                $service->setOutput('tracking/service_other');
                break;
            }

            $shipment = $service->getDeliveryInformation($id);

            $output = // .. create block that contains the view, $output will contain the shipment data; this is returned to the ajax request.
        }
        catch (Exception_RequestError $e)
        {
            ..
        }

        // finally
        $this->getResponse()->setHeader('content-type', 'text/html', true);
        $this->getResponse()->setBody($output);
    }
}
代码缩短了一点,因为有更多的函数,但并不重要

接口两个运输提供商模型正在实施的接口

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

    /**
    * Returns acceptor name
    * @return string
    */
    public function getAcceptorName();
}
服务类处理来自发货模型的请求数据

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;
    }
}
装运类别的示例;在这种情况下,我有两个

class Service_Gls implements Output
{
    const SERVICE_NAME = 'GLS';
    const SERVICE_URL = 'http://www.gls-group.eu/276-I-PORTAL-WEBSERVICE/services/Tracking/wsdl/Tracking.wsdl';

    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;
    }
}
控制器

class AjaxController extends Mage_Core_Controller_Front_Action
{
    public function getDeliveryInformationAction()
    {
        $id = $this->getRequest()->getParam('id', false);
        if ($id && $this->getRequest()->isAjax())
        {
            // SO note: service parameter is just two radio buttons with values "gls", "otherservice"
            $serviceType = $this->getRequest()->getParam('service', false);
            try
            {
                $service = Mage::getModel('tracking/service');

                switch ($serviceType)
                {
                    case 'gls':
                    $service->setOutput('tracking/service_gls');
                    break;

                    case 'other':
                    $service->setOutput('tracking/service_other');
                    break;
                }

                $shipment = $service->getDeliveryInformation($id);

                $output = // .. create block that contains the view, $output will contain the shipment data; this is returned to the ajax request.
            }
            catch (Exception_RequestError $e)
            {
                ..
            }

            // finally
            $this->getResponse()->setHeader('content-type', 'text/html', true);
            $this->getResponse()->setBody($output);
        }
    }
}

您可以使用开关或某种字符串连接来返回所需的strategy类

对于Strategy模式,通常通过StrategyContext模式在运行时选择正确的策略:。这允许您隔离算法以选择正确的策略,这样它就不会“在类中200行深处的某个函数中”

至于设置运行时策略的算法,我个人喜欢使用类常量,而不是字符串操作等。既然游戏的目的是获得要实例化的类名,为什么不使用类常量来返回类名呢

class OutputStrategyContext{
    const SERVICE = 'tracking/service_gls';
    const OTHER = 'tracking/service_other';

    private $strategy;

    public function __construct($serviceType)
    {
        $strategy = constant('self::' . strtoupper($serviceType));
        $modelInstance = Mage::app()->getConfig()->getModelInstance($strategy);
        $this->strategy = $modelInstance;
    }

    public function getStrategy()
    {
        return $this->strategy;
    }
}
轻量级且易于维护,策略类列表位于一个位置

当然,您可以使整个过程保持静态,或者使用另一种设计模式(如抽象工厂方法)来实现相同的事情。真的由你决定

无论如何,在控制器中,它是一个单行程序

class AjaxController extends Mage_Core_Controller_Front_Action
{
    public function getDeliveryInformationAction()
    {
        $id = $this->getRequest()->getParam('id', false);
        if ($id && $this->getRequest()->isAjax())
        {
            // SO note: service parameter is just two radio buttons with values "gls", "otherservice"
            $serviceType = $this->getRequest()->getParam('service', false);
            try
            {
                $service = Mage::getModel('tracking/service');
                $outputModel = new OutputStrategyContext($serviceType)->getStrategy();
                $service->setOutput($outputModel);

                $shipment = $service->getDeliveryInformation($id);

                $output = // .. create block that contains the view, $output will contain the shipment data; this is returned to the ajax request.
            }
            catch (Exception_RequestError $e)
            {
                ..
            }

            // finally
            $this->getResponse()->setHeader('content-type', 'text/html', true);
            $this->getResponse()->setBody($output);
        }
    }
}
当然,您必须修改服务。我还为您的代码修改了上下文类

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.. ;-)
        $this->output = $outputModel;
    }

    /**
     * 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;
    }
}

谢谢你的评论。我同意你的观点。因此,基本上你会建议我添加一个包含我的类和其他逻辑的上下文来返回正确的类,然后将其返回给调用getDeliveryInformation()的服务类?我仍然需要在我的控制器类中执行某种类型的switch/if/then,以便调用上下文tho;这似乎是一种“浪费”,因为我只能在我的服务类中执行逻辑?也许我误解了。创建服务类是为了将逻辑排除在我的控制器之外?不管怎么说,在控制器里,你需要打电话的就是担心,现在是星期天早上,有东西闯入。无论如何,我已经扩展了我的答案。我将在一分钟或20分钟后添加服务MOFD。“框架”是Magento(基于Zend):)。非常感谢你扩展了答案;现在有道理了。最好尽可能多地保留控制器的逻辑,这就是我创建服务类的原因。我喜欢你的方法,并将以此为基础实施。给阅读的其他人快速记下。self::$策略不起作用,因为它正在查找静态属性。使用“constant”函数访问它,类似于:$strategy=constant(sprintf('self::%s',strotupper($serviceType));