从对象中删除太多的IF

时间:2016-02-06 15:55:04

标签: php oop object

编辑:

我应该提到我希望它更加面向对象。而且我不认为我的代码在OO附近,也没有使用交换机,是吗?

OP:

首先,在我的下面的例子中,我正在使用荷兰语单位,所以计算可能看起来不合适,但你会得到这个想法。

基本上,我所拥有的是带产品的杂货清单。在我的数据库中,我将价格存储在"逐个价格"或者"以英镑和#34;为例。因此,为了根据所选金额计算每种产品的总价,我正在使用以下课程。

示例:

在我的杂货店里,我有一些产品,在这个产品背后是文本字段和下拉列表。在文本字段中,我可以输入我想要的金额,在下拉列表中我选择是否需要盎司,磅等等。根据这些价值和我数据库的初始价格(每件价格等),我可以计算每种产品的总价格。

class Calculation
{
    protected $price;
    protected $amount;
    protected $unit;

public function __construct($price, $amount, $unit)
{
    $this->price = $price;
    $this->amount = $amount;
    $this->unit = $unit;
}

public function calculate()
{
    if($this->unit === 'ounce')
    {
        return $this->formatOunce();
    }
    if($this->unit === 'pound')
    {
        return $this->formatPound();
    }
        return $this->formatOne();
}

public function formatOne()
{
    return $this->price * $this->amount / 100;
}

public function formatOunce()
{
    return $this->price / 1000 * $this->amount / 100;
}

public function formatPound()
{
    return $this->price / 1000 * 500 * $this->amount / 100;
}

}

我遇到的问题是:

public function calculate()
{
    if($this->unit === 'ounce')
    {
        return $this->formatOunce();
    }
    if($this->unit === 'pound')
    {
        return $this->formatPound();
    }
        return $this->formatOne();
}

我如何更改上面的代码以使其成为优秀的OO?我是否使用存储库或接口?或者我可以在这个特定的课程中做到这一点,以保持简单?我觉得IF太多了。

4 个答案:

答案 0 :(得分:3)

我建议立即做出两处修改:

  1. 使用类常量而不是硬编码的字符串标识符。优点是双重的:IDE提供更好的自动完成支持,不再有拼写错误。

  2. 使用switch()语句,它更清楚:

  3. class Calculation
    {
        const UNIT_WEIGHT_OUNCE = 'ounce';
        const UNIT_WEIGHT_POUND = 'pound';
        // ...
    
        protected $price;
        protected $amount;
        protected $unit;
        // ...
    
        public function calculate()
        {
            switch ($this->unit) {
                case self::UNIT_WEIGHT_OUNCE: 
                    return $this->formatOunce();
                case self::UNIT_WEIGHT_POUND:
                    return $this->formatPound();
                // ...
                default:
                    return $this->formatOne();
            }
        }
    
        // ...
    }
    

    这也允许有一个"计算目录"和单个方法而不是几个独立的方法进行实际计算,因为你可以将公式存储在数组中,甚至可以再次存储在静态类中......

    最后一个更基本的事情:我想在这里讨论架构方法:

    为什么您选择实施课程Calculation?这对我来说非常直观......实现类似于" PositionInShoppingCart"的东西更自然。代替?那样的对象可以保存产品标识符,基本价格,数量/金额等等?这将导致一个" ShoppingCart"以自然方式进行类...该类型的对象将保存第一类对象的列表。

答案 1 :(得分:1)

你不想切换到开关吗?

首先,在概念上和逻辑上,交换机是正确的方法。您正在使用一个变量$this->unit切换值。我认为看起来真的很干净。 [When to use If-else if-else over switch statments and vice versa]

其次,它更快[Is "else if" faster than "switch() case"?]。虽然在你的情况下,可能并不重要。

$res = NULL;
switch($this->unit)
{
    case 'ounce': $res = $this->formatOunce(); break;
    case 'pound': $res = $this->formatPound(); break;
    default: $res = $this->formatOne();
}
return $res;

答案 2 :(得分:1)

面向OO的方法(在编辑问题后按要求):

创建一个处理输出total()的基类。它调用一个受保护的方法来进行计算。 calculate()

class Item
{
    protected $price;
    protected $amount;

    public function __construct($price, $amount)
    {
        $this->price = $price;
        $this->amount = $amount;
    }

    protected function calculate()
    {
        return $this->price * $this->amount;
    }

    public function total($format = true)
    {
        if ($format) {
            return number_format($this->calculate(), 2);
        }
        return $this->calculate();
    }
}

现在,您可以使用项目的版本扩展您的基本项目。 Pound 版本将覆盖calculate()方法,因为计算方式不同。

class PoundItem extends Item
{
    protected function calculate($format = true)
    {
        return $this->price / 1000 * 500 * $this->amount / 100;
    }
}

要生成对象,您需要构造函数方法或生成它们的工厂。这是一个工厂类。它也可以在你的篮子课上实施。

class ItemFactory
{
    static public function create($price, $amount, $type)
    {
        // this could be implemented in numerous ways
        // it could even just be method on your basket
        $class = $type . "Item";
        return new $class($price, $amount);
    }
}

创建类型的新项目:

$a = ItemFactory::create(49.99, 25, "Pound");

由于PoundItem也是Item,您可以使用total()方法。但是,由于我们已经改变了calculate()的实现,现在它计算了磅数。

echo $a->total();

答案 3 :(得分:0)

你的问题是replacing conditionals with polymorphism,你从每个条件中分出代码并将它们放在自己的类中:Ounce类知道如何计算盎司,Pound类知道如何计算磅。

如前所述,你仍然需要某种factory来确定是否需要使用英镑或盎司。那个工厂看起来......好吧,就像你现在的那样(但很可能在其他地方抽象出来)。

由于目前代码的简单程度,我认为任何这些变化都会过早地进行重构。你现在所拥有的并不复杂 - 理解代码所做的事情并不会让某人放慢速度。一旦你开始有更复杂的方法来计算某些东西的计算,那么查看多态就更合适了。

同样,我认为switch语句与IF条件推荐也不是非常有用的建议。在这种情况下,他们不会添加任何可读性。