Хороший шаблон проектирования для условных операторов, основанный на состоянии нескольких переменных - PullRequest
3 голосов
/ 14 сентября 2011

Я разрабатываю метод расчета итога для корзины покупок, написанный на PHP, и хотел бы получить отзыв о хорошем шаблоне проектирования для обработки различных условий. Я пытаюсь предложить администраторам несколько стратегий для расчета скидок. Администраторам будет предоставлен выбор применить скидки до или после применения налогов, а также применить скидку к доставке или нет. Это дает обзор моей задачи.

Переменные

У меня есть следующие переменные для этой задачи с возможными значениями:

$tax_option: «до», «после»

$shipping_option: «да», «нет»

В дополнение к этим двум переменным формула для расчета общей стоимости будет меняться в зависимости от отношения между $subtotal (количество товаров в корзине) и $reduction (общая сумма, подлежащая дисконтированию) ,

Вообще говоря, моя логика заключается в том, что я проверяю каждую из 4 комбинаций переменных $tax_option и $shipping_option. Мне также нужно изменить формулу для ситуаций, когда $subtotal меньше или равно $reduction. Всего у меня 8 разных условий.

Возможности

Я полагаю, что у меня действительно есть 3 варианта: операторы if, оператор switch или шаблон стратегии. Я покажу структуру оператора if и шаблон стратегии, но исключу возможность switch, потому что в данном контексте это не совсем правильно.

if Заявления

Вот общая схема утверждения if, которое я рассматриваю. Обратите внимание, что я знаю, что мог бы немного перестроить это, но по любой причине это более читабельно для меня.

if($tax_option == 'after' && $shipping_option == 'yes')
{
    if($subtotal <= $reduction)
    {

    }
    else
    {

    }
}
elseif($tax_option == 'before' && $shipping_option == 'yes')
{
    if($subtotal <= $reduction)
    {

    }
    else
    {

    }
}
elseif($tax_option == 'before' && $shipping_option == 'no')
{
    if($subtotal <= $reduction)
    {

    }
    else
    {

    }
}
elseif($tax_option == 'after' && $shipping_option == 'no')
{
    if($subtotal <= $reduction)
    {

    }
    else
    {

    }
}
else
    $new_total = $total;

Шаблон стратегии

Посмотрев в Интернете решение этой проблемы, я узнал о паттерне стратегии , который выглядит потрясающе. Я начал работать над этим здесь и не буду возражать против некоторых отзывов. Пока это выглядит как следующий код с очевидным удалением некоторых подпрограмм.

class DPPCalculateTotal
{
    protected $formulas = array();

    public function DPPCalculateTotal($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {
        foreach($this->formulas as $formula)
        {
            if($formula->test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction))
            {
                return $formula->calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction);
            }
        }
    }

    function add_formula(DPPFormula $formula)
    {
        $this->formulas = $formula;
    }
}

interface DPPFormula
{
    public function test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction);

    public function calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction);
}

class AfterTaxesYesShippingGreaterSubotal implements DPPFormula
{
    public function test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }

    public function calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }
}

class AfterTaxesYesShippingLesserSubotal implements DPPFormula
{
    public function test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }

    public function calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }
}

class AfterTaxesNoShippingGreaterSubotal implements DPPFormula
{
    public function test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }

    public function calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }
}

class AfterTaxesNoShippingLesserSubotal implements DPPFormula
{
    public function test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }

    public function calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }
}

class BeforeTaxesYesShippingGreaterSubotal implements DPPFormula
{
    public function test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }

    public function calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }
}

class BeforeTaxesYesShippingLesserSubotal implements DPPFormula
{
    public function test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }

    public function calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }
}

class BeforeTaxesNoShippingGreaterSubotal implements DPPFormula
{
    public function test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }

    public function calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }
}

class BeforeTaxesNoShippingLesserSubotal implements DPPFormula
{
    public function test($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }

    public function calculate_total($tax_option, $shipping_option, $total, $subtotal, $shipping, $tax, $coupons_amount, $reduction)
    {

    }
}

Вопросы

1) Как вы думаете, что будет лучшим вариантом для продолжения здесь?

2) Каковы преимущества шаблона стратегии в этом случае?

3) Так как я впервые пробую Шаблон стратегии, похоже, я иду в правильном направлении?

Большое спасибо за ваш вклад! Я с удовольствием изучаю этот шаблон и буду благодарен за любые отзывы, которые я смогу получить!

Ответы [ 3 ]

4 голосов
/ 14 сентября 2011

Я думаю, вы идете правильным путем. С помощью шаблона стратегии действительно легко добавлять новые стратегии позже, когда приложение становится больше или меняются требования (что, по моему опыту, имеет место в 9 раз из 10).

Я бы предложил создать еще один класс, называемый чем-то вроде Order, чтобы инкапсулировать все детали вашего заказа и вместо этого передавать объекты вокруг. Ваши тесты и методы расчета будут немного более аккуратными

interface DPPFormula
{
    public function test(OrderInteface $order);

    public function calculate_total(OrderInterface $order);
}

interface OrderInterface
{
    function setTotal($total);
    function getTotal();
}

Вы можете сделать что-то похожее на

$order->setTotal($calculator->DPPCalculateTotal());

В зависимости от вашей сложности вы можете или не можете использовать интерфейс для Order. Я настоятельно рекомендую вам использовать один, так как это еще больше увеличивает абстракцию.

0 голосов
/ 14 сентября 2011

Задумывались ли вы об использовании справочной таблицы / массива? Может сделать вещи немного понятнее для кого-то еще. Ключ массива может быть строкой для различных комбинаций, тогда вы можете связать любые значения, которые вы хотите с ключами поиска.

$lookup = array(
    'taxo-after:shipo-yes'=> array('reduction'=>100),
    'taxo-after:shipo-no'=> array('reduction'=>100),
    'taxo-before:shipo-yes'=> array('reduction'=>100),
    'taxo-before:shipo-no'=> array('reduction'=>100),
    ...
 );

 $lookup_key = 'taxo-'.$tax_option.':'.'shipo-'.$shipping_option;
 if ( $subtotal < $lookup[$lookup_key]['reduction'] ) {
 } else {
 }

Это в значительной степени заменяет весь ваш первый пример кода. 1 объявление массива и 1 оператор if. Вы можете добавить сотни возможностей без потери производительности или большого количества кода.

0 голосов
/ 14 сентября 2011

Я думаю, что было бы возможно разделить итоговую сумму на дисконтируемую сумму и не дисконтируемую сумму. Затем вы применяете скидку к дисконтируемой сумме и добавляете к остальным что-то вроде этого псевдокода:

extra = 0
if (tax_option)
  subtotal += tax(subtotal)
else
  extra += tax(subtotal)
if (delivery_option)
  subtotal += delivery
else
  extra += delivery
if (subtotal > reduction)
  subtotal -= reduction
else
  // stuff here
subtotal += extra
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...