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

发布于 2024-12-04 13:48:32 字数 5596 浏览 2 评论 0原文

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

变量

对于此任务,我有以下变量以及可能的值:

$tax_option : 'before', 'after'

$shipping_option : ' yes', 'no'

除了这两个变量之外,计算总价值的公式也会根据 $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;

策略模式

在网上寻找此问题的解决方案后,我了解到策略模式,看起来棒极了。我在这里开始研究它,不介意一些反馈。到目前为止,它看起来像下面的代码,明显删除了一些例程。

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)由于这是我第一次尝试策略模式,看起来我的方向正确吗?

非常感谢您的投入!我很高兴学习这种模式,并且非常感谢我能得到的任何反馈!

I'm working out a method to calculate a total for a shopping cart written in PHP and would like some feedback on a good design pattern for handling the different conditions. I am trying to offer the admins multiple strategies for calculating discounts. Admins will be given the choice to apply discounts before or after taxes are applied, as well as apply the discount to the shipping or not. This gives an overview of my task.

The Variables

I have the following variables for this task with the possible values:

$tax_option : 'before', 'after'

$shipping_option : 'yes', 'no'

In addition to these two variables, the formula for calculating the total value will change depending on the relationship between $subtotal (the amount of the items in the cart) and the $reduction (the total amount to be discounted).

Generally speaking, my logic is that I test for each of the 4 combinations of the $tax_option and $shipping_option variables. I also need to change the formula for situations where the $subtotal is less than or equal to the $reduction. In all, I have 8 different conditions.

The Possibilities

I figure that I really have 3 different options here: if statements, a switch statement, or a strategy pattern. I'll show the structure of the if statement and the strategy pattern, but will exclude the switch possibility because that just does not seem right in this context.

if Statements

Here is the general pattern of the if statement that I am considering. Note that I know I could restructure this slightly, but for whatever reason this is more readable for me.

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

After looking around online for a solution to this problem, I learned about the Strategy Pattern, which looks awesome. I started work on it here and wouldn't mind some feedback on it. So far it looks like the following code, with obvious deletion of some of the routines.

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)
    {

    }
}

The Questions

1) In your opinion, what would be the best way to proceed here?

2) What are the advantages of the Strategy Pattern in this instance?

3) As this is the first time I'm trying out the Strategy Pattern, does it look like I'm heading in the right direction?

Thank you so much for your input! I'm having fun learning this pattern and will appreciate any feedback I can get!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

停顿的约定 2024-12-11 13:48:32

我认为你的方向是正确的。使用策略模式,当应用程序变得更大或需求发生变化时,以后添加新策略真的很容易(根据我的经验,十有八九是这种情况)。

我建议再创建一个名为 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 的接口。我强烈建议您使用一个,因为这进一步增加了抽象性。

I think you're heading the right way. With the strategy pattern it's really easy to add new strategies later on as the application gets bigger or the requirements change (which in my experience is the case 9 times out of 10).

What I would suggest is making one more class called something like Order to encapsulate all your order details in there and pass the objects around instead. Your tests and calculation methods would be a bit tidier

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

    public function calculate_total(OrderInterface $order);
}

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

You could then do something similar to

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

Depending on your complexity you may or may not want to use an interface for the Order. I strongly suggest you do use one as this further increases the abstraction.

紅太極 2024-12-11 13:48:32

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

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

I think it would be possible to split the total up into a discountable amount and a not discountable amount. You then apply the discount to the discountable amount, and add on the rest, something like this pseudocode:

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
夏尔 2024-12-11 13:48:32

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

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

Have you thought about using a lookup table/array? Might make things a little clearer to someone else. The array key could be the string for the various combinations, then you can associate any values you want to the lookup keys.

$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 {
 }

That pretty much replaces your entire first code example. 1 array declaration and 1 if statement. You can add a hundred possibilities without any performance penalty or much more code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文