switch (get_class($item))
часть кода является некорректная реализация концепции ООП называется polymorphism.
правильный способ сделать это заключается в определении interface, объявляющий метод (назовем его updateAmounts()
), а затем реализовать интерфейс во всех классах вы можете иметь в качестве $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
и FeeItem
уже распространяется тот же базовый класс (или имеют общего предка), то этот метод может быть добавлен (как abstract
или с пустой реализацией) к этому классу (и интерфейс больше не нужен).
После указанного преобразования кода, вы все еще хотите, чтобы найти способ повторного использования вложенных 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);
}
}
}
}
If $itemGroup
является объектом некоторого класса (или более), то внутренний foreach
может быть перемещен в каждый из этих классов (в ее реализации методы updateAmounts()
), и таким образом код остается в том же классе с данными, которые он обрабатывает (он называется encapsulation, и это еще одно важное свойство кода ООП).
Теперь вызывающий код теперь выглядит следующим образом:
$amounts = new Amounts();
foreach ($this->orders as $order) { // OrderObject
$order->updateAmounts($amounts);
}
echo('Revenue: '.$amounts->revenue."\n");
echo('Fees: '.$amounts->fees."\n");
Смотри, ма! Нет больше вложенной foreach
петли
Я боюсь, что я разорвал свой пакет вложенных foreach
петель и нет ничего, чтобы повторно использовать из него.
Но подождите! В ваших классах теперь есть поведение (они, вероятно, были просто аморфными контейнерами данных), и это лучше, чем писать универсальный код (поскольку, похоже, это была ваша цель повторного использования циклов foreach
). Потому что это то, что предполагается ООП: данные и код упакованы вместе.
возможно анонимные функции? просто передайте ему экземпляр '$ this', если вы хотите его использовать. – KDOT
Не для чего предназначены' функции'? – Nytrix
@nytrix Можете ли вы объяснить подробнее? – iakkam