我有一个嵌套循环来迭代由不同对象类型组成的数据结构。我想将此循环结构用于多种用途。
我找到了关于how to reuse nested loops in C++的讨论。有没有办法在PHP中重用这个循环结构而不将循环复制到每个不同情况的新函数中?
这是我的代码:
foreach ($this->orders as $order) { // OrderObject
foreach ($order->items as $itemGroup) { // ItemPrice, ItemFees
foreach ($itemGroup as $item) { // PrincipalItem, PriceItem, FeeItem
switch (get_class($item)) {
case 'PrincipalItem':
$revenue += $item->getAmountAfterTax() * $item->quantity;
break;
case 'PriceItem':
$revenue += $item->getAmountAfterTax();
break;
case 'FeeItem':
$fees += $item->amount;
break;
}
}
}
}
在上面的第一个例子中,我正在总结各种值。在另一个例子中,我想以CSV格式输出数据。相同的循环将实现此目的,我只需要更改开关盒内的线。
答案 0 :(得分:1)
代码的switch (get_class($item))
部分是名为polymorphism的OOP概念的错误实现。
正确的方法是定义一个interface来声明一个方法(让它命名为updateAmounts()
)然后在你可以拥有的所有类中实现接口{{1在内循环中。 $item
方法的每个实现都包含来自updateAmounts()
语句的相应case
的代码。
这样的事情:
switch
现在嵌套循环看起来像这样:
// This class is a Value Object.
// It doesn't need behaviour and it's OK to have public properties
class Amounts
{
public $revenue = 0;
public $fees = 0;
}
interface HasAmounts
{
/**
* Update the revenue and the fees in the passed Amounts object
* using the data stored in this object.
*/
public function updateAmounts(Amounts $a);
}
class PrincipalItem implements HasAmounts
{
public function updateAmounts(Amounts $a)
{
$a->revenue += $this->getAmountAfterTax() * $this->quantity;
}
}
class PriceItem implements HasAmounts
{
public function updateAmounts(Amounts $a)
{
$a->revenue += $this->getAmountAfterTax();
}
}
class FeeItem implements HasAmounts
{
public function updateAmounts(Amounts $a)
{
$a->fees += $this->amount;
}
}
如果类$amounts = new Amounts();
foreach ($this->orders as $order) { // OrderObject
foreach ($order->items as $itemGroup) { // ItemPrice, ItemFees
foreach ($itemGroup as $item) { // PrincipalItem, PriceItem, FeeItem
$item->updateAmounts($amounts);
}
}
}
echo('Revenue: '.$amounts->revenue."\n");
echo('Fees: '.$amounts->fees."\n");
,PrincipalItem
和PriceItem
已经扩展了相同的基类(或具有共同的祖先),则可以添加该方法(如abstract
或使用空实现)到这个类(并且不再需要接口)。
在上面的代码转换之后,你还想找到一种方法来重用嵌套的FeeItem
块吗?
foreach
声明现在看起来不值得重用。发生这种情况是因为值得重用的代码被移动到它所属的位置;代码应该与它处理的数据保持在一起。
通过移动包含其操作数据的类中的每个foreach
,可以对代码进行进一步的重构。
例如:
foreach
如果class OrderObject implements HasAmounts
{
public function updateAmounts(Amounts $a)
{
foreach ($this->items as $itemGroup) { // ItemPrice, ItemFees
foreach ($itemGroup as $item) { // PrincipalItem, PriceItem, FeeItem
$item->updateAmounts($amounts);
}
}
}
}
是某个类(或更多)的对象,那么内部$itemGroup
可以移动到这些类中的每一个(在foreach
方法的实现中)代码与它处理的数据保持在同一个类中的方式(它被称为encapsulation,它是OOP代码的另一个重要属性)。
现在调用代码现在看起来像这样:
updateAmounts()
看,妈!没有嵌套的$amounts = new Amounts();
foreach ($this->orders as $order) { // OrderObject
$order->updateAmounts($amounts);
}
echo('Revenue: '.$amounts->revenue."\n");
echo('Fees: '.$amounts->fees."\n");
循环
我担心我会破坏你的嵌套foreach
循环,没有什么可以重用它。
但是等等!你的类现在包含行为(它们可能只是数据的无定形容器),这比编写通用代码更好(因为它似乎是你重用foreach
循环的目标)。因为这就是OOP应该是:数据和代码打包在一起。
答案 1 :(得分:0)
如果您想多次使用某段代码,请制作functions
。然后可以执行那些。这是函数的manual。
示例:的
<?php
function example($itemGroup,$orders,$items){
foreach ($orders as $order) { // OrderObject
foreach ($items as $itemGroup) { // ItemPrice, ItemFees
foreach ($itemGroup as $item) { // PrincipalItem, PriceItem, FeeItem
switch (get_class($item)) {
case 'PrincipalItem':
$revenue += $item->getAmountAfterTax() * $item->quantity;
break;
case 'PriceItem':
$revenue += $item->getAmountAfterTax();
break;
case 'FeeItem':
$fees += $item->amount;
break;
}
}
}
}
}
example($value,$oders,$items);
?>
您现在可以在任何地方调用此example
。如果您在OOP
写作,请参阅手册。
如前所述,您还可以将functions
存储在variable
中,这些名为Anonymous functions
。以下是对manual
实施例
<?php
$var = function($variable){
echo "this is a function with a ".$variable;
}
$var("variable");
?>
您可以调用代码中显示的那些函数。
所有这些都会导致你的答案,你可以把它们融合在一起,就像这样。
实施例
<?php
function test( callable $function){
$function();
}
test(function(){
echo "some code";
})
?>
在您的情况下:
<?php
function example($itemGroup,$orders,$items,$function()){
foreach ($orders as $order) { // OrderObject
foreach ($items as $itemGroup) { // ItemPrice, ItemFees
foreach ($itemGroup as $item) { // PrincipalItem, PriceItem, FeeItem
$function();
}
}
}
}
example($itemGroup,$orders,$items,function(){
switch (get_class($item)) {
case 'PrincipalItem':
$revenue += $item->getAmountAfterTax() * $item->quantity;
break;
case 'PriceItem':
$revenue += $item->getAmountAfterTax();
break;
case 'FeeItem':
$fees += $item->amount;
break;
}
}
?>