2015-04-26 220 views
2

我可能会执行此错误,因为我无法找出设置策略模式实施策略的可靠方法。我不是一个“静态”编写的粉丝,也许还有另一种方式。在策略模式中设置策略

背景故事:我为了检索任何用户输入前端跟踪&跟踪信息做了两(2)实现(SOAP + HTTP)航运商。他们每个人都遵循一个接口,以便我知道哪些功能是和应该(PHP:3)可用。我已经缩短了下面的类名,因为这是Magento,类名非常长。

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

我的挑战:我使用开关盒来设置跟踪/ service_gls和跟踪/ service_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); 
     } 
    } 
} 

回答

1

那么你要么交换机或带有某种字符串连接的返回你所需要的战略类做到这一点。

使用策略模式,在运行时选择正确的策略通常通过StrategyContext模式完成:https://sourcemaking.com/design_patterns/strategy/php。这可以让你隔离算法来选择正确的策略,所以它不是“在某个功能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; 
    } 
} 
+0

感谢您的意见。我跟你常在一起。所以基本上你会建议我添加一个包含我的类和其他逻辑的上下文,以返回正确的类,然后将其返回给调用getDeliveryInformation()的Service类?我仍然需要在我的Controller类中进行某种switch/if /然后调用Context tho;这似乎是一种“浪费”,因为我可以在我的Service类中执行逻辑?也许我是误解。创建Service类是为了使逻辑不在我的Controller中。 – Michael

+0

Wel我假设你正在使用服务容器:顺便提一下框架?无论如何,在控制器,你需要拨打的是 –

+0

对不起,这是星期天morninh在这里和东西侵入。无论如何,我已经延长了我的答案。我会在一分钟或20分钟内添加服务mofds。 –