Возможно, я неправильно это реализовал, поскольку не могу найти надежный способ определить, какую стратегию использовать в моей реализации шаблона стратегии. Я не большой поклонник написания этого «статически», возможно, есть другой способ.
Предыстория: Я сделал две (2) реализации (мыло + http) для провайдеров доставки, чтобы получить трек & Информация трассировки для любого пользовательского интерфейса. Каждый из них следует интерфейсу, так что я знаю, какие функции есть и должны (PHP: 3) быть доступными. Я сократил имена классов ниже, так как это Magento, а имена классов очень длинные.
Поток: Клиент вводит номер отслеживания в форму и отправляет. Запрос отправляется на контроллер, контроллер инициализирует экземпляр класса Service, устанавливает вывод через. $ service-> setOutput (‘tracking / service_gls’) — обратите внимание, что tracking / service_gls просто отображается непосредственно на класс обслуживания (вещь Magento), вызывается $ service-> getDeliveryInformation ($ number) (мы знаем, что это существует из-за интерфейса ), весь объект $ service возвращается в представление и данные представляются.
Моя задача: Я использую случай переключения, чтобы установить tracking / service_gls и tracking / 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);
}
}
}
Что ж, вы либо делаете это с помощью переключателя, либо с помощью некоторой конкатенации строк, чтобы получить нужный вам класс стратегии.
С помощью шаблона стратегии выбор правильной стратегии во время выполнения обычно выполняется с помощью шаблона 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;
}
}
Других решений пока нет …