2015-04-26 324 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。 –