0

以下 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”元素之前。

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

4

3 回答 3

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

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

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

于 2012-10-19T14:13:50.730 回答
0

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

于 2012-10-19T13:22:15.807 回答
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 并没有让这变得容易,您最终会编写大量代码以获得可疑的好处。

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

于 2012-10-19T13:28:04.530 回答