2016-11-20 3 views
1

У меня есть вложенный цикл для итерации по структуре данных, состоящей из разных типов объектов. Я хотел бы использовать эту структуру цикла для нескольких целей.Повторное использование структуры вложенных циклов PHP для нескольких целей

Я нашел дискуссию о 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

возможно анонимные функции? просто передайте ему экземпляр '$ this', если вы хотите его использовать. – KDOT

+0

Не для чего предназначены' функции'? – Nytrix

+0

@nytrix Можете ли вы объяснить подробнее? – iakkam

ответ

1

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). Потому что это то, что предполагается ООП: данные и код упакованы вместе.

+0

Большое вам спасибо за этот ответ. Я многому научился. Я сожалею о задержке в ответе ... Я действительно думал об этом и не успел исправить мой код. – iakkam

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; 
    } 

} 
?> 
+0

Спасибо за ваш ответ. Я понимаю функции, но я не уверен, что этот ответ помогает. Ваша функция содержит мой вложенный цикл, но сохраняет строки внутри операторов switch/case, которые я хотел бы изменить в зависимости от моей цели. Линии, которые я хотел бы изменить, такие, как '$ income + = $ item-> getAmountAfterTax() * $ item-> quantity;' – iakkam

+0

@iakkam, ваш вопрос состоял в том, как повторить определенный фрагмент кода. Я дал вам ответ. Что вы хотите изменить тогда? – Nytrix

+0

Я хочу повторно использовать цикл, но выполняю различные операторы внутри коммутатора/case ... например, $ income + = $ item-> getAmountAfterTax() * $ item-> quantity; 'может стать чем-то совершенно другим, как' $ this-> convert2CSV ($ item); ' – iakkam