我实现了一个小的IdentityMap到我的DataMappers,它的工作方式正确,它知道如果一个对象已经加载,但它不分配内存对象正确。
我已经简化了代码,尽可能多的我可以(它并不复杂)到一个实体,没有数据库等。有人可以解释为什么在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:
为什么第二个和第三个Customer对象没有名称?我很确定在lookup()方法中的分配部分存在问题。从昨晚开始,我就一直在做这件事,试着阅读所有的东西。
我已经改变了lookup()方法的签名,在传入的对象前面有"&"符号。
问题是
当fetch()在第一个循环中被调用时,它又调用lookup(),它不会找到任何值(因为identityMap是空的),结果$customer将在assign()中被赋予新值(在这种情况下$customer->name =' John'和$customer->id='1')。注意,$customer->setId(1);
没有提供id。无论给$this->assign()
赋什么值,都会通过将id值赋给1来修改$customer的原始id值(通过引用传递)。您可以通过将1更改为任意值来测试它(例如,如果您将1更改为3,您将看到所有结果)。
因此,在第一个循环中,$customer填充了所有要正确显示的值(id->1和name->'john')
但是在第二个循环中
if( $this->identityMap->lookup($customer) ) {
return true;
}
返回true。(在$identityMap中找到id =1的客户对象;因此,它没有修改作为参数传递的$customer对象。)这意味着,函数在name value被赋值给$customer之前返回。
从第二个循环开始
for( $i = 0; $i < 3; $i++ ){
...
$customer->setId(1);
...
}
新创建的$customer对象将不会被分配name value。这就是为什么它只显示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'));
您正在使用相同的键(id)向查找添加3个客户在第一次for循环运行之后,fetch方法在for循环运行的其余部分返回true。因此,该名称将永远不会被设置。
你可以试试:
if( $this->identityMap->lookup($customer) ) {
return $this->identityMap->get($customer);
}
但是不要忘记在IdentityMap类中实现"get"方法;)