处理顺序错误的相关元素

时间:2012-10-19 13:18:30

标签: php

以下PHP示例代码仅在“bar”元素位于“foo”元素之前时才有效。如果它们的顺序错误,我将得到“对非对象的成员函数调用”错误。

$data = array();
foreach($elems as $e) {
  if($e['type'] == "foo") {
    $data[$e["key"]->foo_data($e["data_foo"]);
  }
  elseif($e['type'] == "bar") {
    $data[$e["key"]] = new Bar($e);
  }
}

我目前的解决方案是在$elems内迭代两次。另一种解决方案是使用usort和自定义排序函数,将“bar”元素放在“foo”元素之前。

是否有任何编程模式或库可以让我按任意顺序处理元素?

3 个答案:

答案 0 :(得分:0)

循环两次似乎是最明智的解决方案。循环一次以准备对象,第二次循环以让这些对象处理数据。可能存在完全不同的,更好的解决方案,但如果没有看到更具体的用例,就很难说。

答案 1 :(得分:0)

一种实用的方法是使用一个中间数据表示,该表示能够容忍出现乱序的内容,然后对数据进行另一次传递以完成它,例如:

foreach($elems as $e) {
  if($e['type'] == "foo") {
    $data[$e["key"]]["foo_data"] = $e["data_foo"];
  }
  elseif($e['type'] == "bar") {
    $data[$e["key"]]["bar"] = new Bar($e);
  }
}

foreach ($data as &$d) {
    $foo_data = $d["foo_data"];
    $d = $d["bar"];
    $d->foo_data($foo_data);
}

类似的替代方案是逐步修复数据,这不需要另一个循环,但更糟糕的是因为它将修正逻辑与您的实际数据提取逻辑耦合并违反了DRY:

foreach($elems as $e) {
  if($e['type'] == "foo") {
    if (isset($data[$e["key"]])) {
        $data[$e["key"]]->foo_data($e["data_foo"]); // normal operation
    }
    else {
        $data[$e["key"]] = $e["data_foo"]; // temporary result
    }

  }
  elseif($e['type'] == "bar") {
    if (isset($data[$e["key"]])) {  // incremental fix
      $bar = new Bar($e); 
      $bar->foo_data($data[$e["key"]]);
      $data[$e["key"]] = $bar;
    else {
      $data[$e["key"]]["bar"] = new Bar($e); // normal operation
    }
  }
}

最后,您当然可以选择其他(精心设计的)方法来避免再次循环并编写“错误代码”,但是PHP并没有那么简单,您最终会编写大量代码以获得可疑的好处。

一般建议:保留临时数据表示并最后再次循环。

答案 2 :(得分:0)

好的,这是一个使用代理模式的解决方案:

class BarProxy {
    private $callstack = array();
    public function __call($name, $arguments) {
        $this->callstack[] = array($name, $arguments);
    }

    public function createBar($params) {
        $bar = new Bar($params);
        foreach($this->callstack as $call) {
            call_user_func_array(array($bar, $call[0]), $call[1]);
        }
        return $bar;
    }
}

class BarData {
    private $data = array();

    public function getBar($id) {
        if(!isset($this->data[$id])) {
            $this->data[$id] = new BarProxy();
        }
    }

    public function createBar($id, $params) {
        if(!isset($this->data[$id])) {
            $this->data[$id] = new Bar($params);
        }
        elseif($this->data[$id] instanceof BarProxy) {
            $this->data[$id] = $this->data[$id]->createBar($params);
        }
    }

    // Other methods for getting the data, maybe implement Iterator or ArrayAccess 
}


$data = new BarData();
foreach($elems as $e) {
  if($e['type'] == "foo") {
    $data->getBar($e["key"])->foo_data($e["data_foo"]);
  }
  elseif($e['type'] == "bar") {
    $data->createBar($e["key"], $e);
  }
}

Pro:此代码可避免使用if语句乱丢for循环。这些类将在他们自己的文件中,循环保持干净且易于理解。可以进一步抽象这两个类来代理和存储任意类。

Con:这个解决方案是以函数调用和另外两个类为代价的,因此性能和内存消耗可能比仅循环两次更糟糕。