2013-06-03 151 views
2

我在DataMapper中实现了一个小型IdentityMap,并且它以它知道对象是否已经加载的方式正常工作但它不会正确分配内存对象。将对象传递给方法,将该对象分配给另一个对象,传递给对象仍然是我传入的同一对象

我已经尽可能地简化了代码(它并不复杂)到一个实体,没有数据库等。有人可以解释为什么在lookup()方法没有正确地分配已经加载的Customer对象在Customer对象中传入?

Customer.php

class Customer { 

    private $id; 
    private $name; 

    public function getId() { 
     return $this->id; 
    } 
    public function setId($id) { 
     $this->id = $id; 
    } 

    public function getName() { 
     return $this->name; 
    } 
    public function setName($name) { 
     $this->name = $name; 
    } 

} 

CustomerMapper

class CustomerMapper { 

    private $identityMap; 

    public function __construct(IdentityMap $identityMap) { 
     $this->identityMap = $identityMap; 
    } 

    public function fetch(Customer $customer) { 

     if($this->identityMap->lookup($customer)) { 
      return true; 
     } 

     $this->assign($customer, array('id' => 1, 'name' => 'John')); 
    } 

    private function assign(Customer $customer, Array $row) { 

     $customer->setId($row['id']); 
     $customer->setName($row['name']); 

     $this->identityMap->add($customer); 
    } 

} 

IdentityMap

class IdentityMap { 

    private $customers; 

    public function lookup(Customer $customer) { 

     if(!array_key_exists($customer->getId(), $this->customers)) { 
      return false; 
     } 

     $customer = $this->customers[$customer->getId()]; //Something wrong here? 

     return true; 
    } 

    public function add(Customer $customer) { 
     $this->customers[$customer->getId()] = $customer; 
    } 

} 

当我然后运行这个:

$identityMap = new IdentityMap(); 
$customerMapper = new CustomerMapper($identityMap); 

for($i = 0; $i < 3; $i++){ 

    $customer = new Customer(); 
    $customer->setId(1); 

    $customerMapper->fetch($customer); 

    echo 'ID: ' . $customer->getId() . '<br>Name: ' . $customer->getName() . '<br><br>'; 

} 

输出:

ID: 1 
Name: John 

ID: 1 
Name: 

ID: 1 
Name: 

为什么第二个和第三个顾客对象没有名字?我相当确定lookup()方法中的分配部分存在问题。自从昨晚尝试和阅读所有内容以来,我一直都在这里。

我已将lookup()方法签名更改为在传入的对象前面有“&”符号,但没有运气。

+1

我们必须更深入。就像一个观点! – Dropout

+0

试试这个,'公共函数查找(Customer&$ customer)',以便它通过引用而不是按值传递。 –

+0

@MichaelPerrenoud尝试过,没有运气。还有其他建议吗?它应该在理论上工作,但不应该呢?我不知道什么是错的。 – ibanore

回答

1

问题是

当取()被调用第一个循环并且它依次调用lookup()它将找不到任何值(因为identityMap为空),因此$ customer将在assign()中获得新值(在这种情况下,$ customer-> name ='John '和$ customer-> id ='1')。请注意,$customer->setId(1);不提供此ID。无论您给$this->assign()的任何值$this->assign()通过将id值指定为1来修改$ customer的原始id值(通过引用传递)。您可以通过将1更改为任意值来测试它(如果将1更改为3,则会显示所有结果)。

所以在第一循环$客户填充了所有正确显示(ID-> 1,名称为 - >“约翰”)的值

但在第二循环

if($this->identityMap->lookup($customer)) { 
    return true; 
} 

回报真正。 (id = 1的客户对象位于$ identityMap中;因此它不会修改作为参数传递的$ customer对象。) 这意味着函数在名称值分配给$ customer之前返回。

因此,从第二循环

for($i = 0; $i < 3; $i++){ 
... 
$customer->setId(1); 
... 
} 

新创建的$客户对象将不会被分配的名称值开始。这就是为什么它仅以id值显示。

可以通过应用以下更改解决上述问题:

function lookup(){ 
... 
return $customer; // instead of returning true 
} 

function fetch(){ 
... 
$c=$this->identityMap->lookup($customer);// 
if($c){ 
    $customer->name=$c->getName(); 
} 

// if you like the new objects hold their original value do the following 
$this->assign($customer, array('id' => $customer->getId(), 'name' => 'John')); 
+0

感谢您的回复。我现在让我的fetch()方法返回对象,而不是仅仅返回true或false。 – ibanore

1

在第一次for循环运行之后,您将3个客户添加到查找中,使用相同的键(id) ,fetch方法对其余的for循环运行返回true。 所以这个名字永远不会被设置。

你可以试试这个:

if($this->identityMap->lookup($customer)) { 
     return $this->identityMap->get($customer); 
    } 

但是不要忘记实现在IdentityMap类中的方法 “搞定”;)

+0

我不是因为一旦下次运行fetch()时添加了ID = 1的客户,它就会找到ID = 1的客户并返回true。 – ibanore

+0

为什么不显示名称?这是问题的一部分。 – Bere

+0

因为您不会从查找中加载数据。你只需返回true,你拥有的只是一个只有id集的客户对象。 –