基于多变量状态的条件语句的良好设计模式

时间:2011-09-13 22:52:24

标签: php conditional strategy-pattern

我正在研究一种方法来计算用PHP编写的购物车的总数,并希望获得一些关于处理不同条件的良好设计模式的反馈。我正在尝试为管理员提供多种计算折扣的策略。管理员可以选择在税收申请之前或之后应用折扣,以及是否将折扣应用于运费。这概述了我的任务。

变量

此任务的以下变量包含可能的值:

$tax_option:'之前','之后'

$shipping_option:'是','不'

除了这两个变量之外,计算总价值的公式将根据$subtotal(购物车中的商品数量)与$reduction(总金额)之间的关系而变化打折)。

一般来说,我的逻辑是我测试$tax_option$shipping_option变量的4种组合中的每一种。我还需要在$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;

策略模式

在网上寻找解决这个问题的方法之后,我了解了Strategy Pattern,它看起来很棒。我在这里开始研究它并且不介意对它的一些反馈。到目前为止,它看起来像下面的代码,明显删除了一些例程。

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 个答案:

答案 0 :(得分:4)

我认为你正朝着正确的方向前进。随着策略模式的推出,随着应用程序变大或需求发生变化(根据我的经验,情况就是9次中的9次),很容易在以后添加新策略。

我建议再做一个名为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的界面。我强烈建议你使用一个,因为这进一步增加了抽象。

答案 1 :(得分:0)

我认为可以将总额分成可折扣金额和不可折扣金额。然后,您将折扣应用于可折扣金额,并添加其余部分,例如此伪代码:

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

答案 2 :(得分:-1)

您是否考虑过使用查找表/数组?可能会让别人更清楚一点。数组键可以是各种组合的字符串,然后您可以将所需的任何值与查找键相关联。

$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语句。您可以添加一百种可能性而不会造成任何性能损失或更多代码。