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


A good design pattern for conditional statements based on the status of multiple variables

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

变量

对于这个任务,我有以下变量和可能的值:

$tax_option:之前、之后

$shipping_option:"是","否">

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

一般来说,我的逻辑是对$tax_option$shipping_option变量的4个组合中的每一个进行测试。对于$subtotal小于或等于$reduction的情况,我还需要更改公式。总之,我有8种不同的情况。

可能性

我想我在这里确实有三种不同的选择: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( 由于这是我第一次尝试战略模式,看起来我朝着正确的方向前进了吗?

非常感谢您的投入!我学习这个模式很开心,如果能得到任何反馈,我将不胜感激!

我认为你正朝着正确的方向前进。有了策略模式,以后随着应用程序的扩大或需求的变化(根据我的经验,10次中有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的接口。我强烈建议您使用一个,因为这会进一步增加抽象性。

我认为可以将总额分为可贴现金额和不可贴现金额。然后,您将折扣应用于可折扣的金额,并将其余金额相加,类似于以下伪代码:

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

您想过使用查找表/数组吗?可能会让其他人更清楚一些。数组键可以是各种组合的字符串,然后您可以将任何想要的值与查找键相关联。

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