编辑:
我应该提到我希望它更加面向对象。而且我不认为我的代码在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太多了。
答案 0 :(得分:3)
我建议立即做出两处修改:
使用类常量而不是硬编码的字符串标识符。优点是双重的:IDE提供更好的自动完成支持,不再有拼写错误。
使用switch()
语句,它更清楚:
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条件推荐也不是非常有用的建议。在这种情况下,他们不会添加任何可读性。