Removing too many IF's from object
编辑:
我应该提到我希望它更面向对象。我不认为这里的代码在OO附近,也不使用开关,是吗?
OP:
首先,在下面的示例中,我使用的是荷兰语单位,因此计算看起来可能不正确,但您会明白这一点。
基本上,我有一份杂货店的产品清单。例如,在我的数据库中,我将价格存储在"按件价格"或"按磅价格"中。因此,为了计算每种产品的总价格,根据所选的数量,我正在处理下面的类。
例子:
在我的杂货店列表中,我有一些产品,在这个产品后面是一个文本字段和一个下拉列表。在文本字段中,我可以输入我想要的数量,并在下拉列表中选择是否需要盎司、磅等。根据这些值和我的数据库中的初始价格(每件价格等),我可以计算出每个产品的总价格。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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; } } |
我的问题是:
1 2 3 4 5 6 7 8 9 10 11 12 | public function calculate() { if($this->unit === 'ounce') { return $this->formatOunce(); } if($this->unit === 'pound') { return $this->formatPound(); } return $this->formatOne(); } |
如何更改上述代码以使其成为良好的OO?我是使用存储库还是使用接口?或者我可以在这个特定的类中这样做来保持简单吗?我觉得有太多如果有的话。
我建议立即进行两次修改:
使用类常量而不是硬编码的字符串标识符。其优点是双重的:IDES能够更好地支持自动完成,并且不再出现打字错误。
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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(); } } // ... } |
这也将允许有一个"计算目录"和一个方法,而不是几个独立的方法来进行实际的计算,因为您可以将公式存储在一个数组甚至静态类中…
最后一件更基本的事情:我想在这里讨论架构方法:
为什么选择实现类
面向OO的方法(编辑问题后按要求):
您将生成一个处理输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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版本将覆盖
1 2 3 4 5 6 7 | class PoundItem extends Item { protected function calculate($format = true) { return $this->price / 1000 * 500 * $this->amount / 100; } } |
要生成对象,您需要一个构造函数方法或所谓的工厂来生成它们。这是工厂班。它也可以在您的basket类上实现。
1 2 3 4 5 6 7 8 9 10 | 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); } } |
创建磅类型的新项目:
1 | $a = ItemFactory::create(49.99, 25,"Pound"); |
由于
1 | echo $a->total(); |
你不想换成开关吗?
首先,在概念上和逻辑上,切换是正确的方式。您正在使用一个变量
其次,它更快[如果"else"比"switch()case"快吗?]虽然在你的情况下,可能没有那么重要。
1 2 3 4 5 6 7 8 | $res = NULL; switch($this->unit) { case 'ounce': $res = $this->formatOunce(); break; case 'pound': $res = $this->formatPound(); break; default: $res = $this->formatOne(); } return $res; |
你的问题是用多态性替换条件,这样你就从每个条件中分离出代码,并把它们放到自己的类中:盎司类知道如何计算盎司,磅类知道如何计算磅。
如前所述,你仍然需要某种工厂来决定是时候使用磅还是盎司。那家工厂看起来…嗯,就像你现在的样子(但很可能是从别处抽象出来的)。
由于目前的代码有多简单,我认为这些更改中的任何一个都将被重构得太早。你现在所拥有的并不复杂——理解代码的作用不会让这里的人放慢脚步。一旦你开始有更复杂的方法来计算某个东西,那么观察多态性将是更好的选择。
同样,我认为switch语句vs if条件建议也不是很有用的建议。在这种情况下,它们不会为可读性添加任何内容。