如何使用Data Mapper模式惰性加载子对象


How to load child objects lazily with the Data Mapper pattern?

如果我有一个相当复杂的用户模型,我想使用数据映射模式来加载,我如何在不允许用户知道UserMapper的情况下惰性地加载一些更密集的用户信息?

例如,如果User模型允许一个Address对象数组(并且User可能有许多这样的对象,但不一定预先需要),如果/当需要时,我如何加载这些对象?

我是否让用户模型意识到AddressMapper?

我是否将用户模型传递回UserMapper,然后只对地址进行补水?

有更好的选择吗?

嗯,我曾经发现了下面这个聪明的模式,由Zend框架的开发人员Ben Scholzen提供。它是这样的:

class ModelRelation
    implements IteratorAggregate
{
    protected $_iterator;
    protected $_mapper;
    protected $_method;
    protected $_arguments;
    public function __construct( MapperAbstract $mapper, $method, array $arguments = array() )
    {
        $this->_mapper    = $mapper;
        $this->_method    = $method;
        $this->_arguments = $arguments;
    }
    public function getIterator()
    {
        if( $this->_iterator === null )
        {
            $this->_iterator = call_user_func_array( array( $this->_mapper, $this->_method ), $this->_arguments );
        }
        return $this->_iterator;
    }
    public function __call( $name, array $arguments )
    {        
        return call_user_func_array( array( $this->getIterator(), $name ), $arguments );
    }
}

Ben Scholzen的实际实现在这里。

你可以这样使用它:

class UserMapper
    extends MapperAbstract
{
    protected $_addressMapper;
    public function __construct( AddressMapper $addressMapper )
    {
        $this->_addressMapper = $addressMapper;
    }
    public function getUserById( $id )
    {
        $userData = $this->getUserDataSomehow();
        $user = new User( $userData );
        $user->addresses = new ModelRelation(
            $this->_addressesMapper,
            'getAddressesByUserId',
            array( $id )
        );
        return $user;
    }
}
class AddressMapper
    extends MapperAbstract
{
    public function getAddressesByUserId( $id )
    {
        $addressData = $this->getAddressDataSomehow();
        $addresses = new SomeAddressIterator( $addressData );
        return $addresses;
    }
}
$user = $userMapper->getUserById( 3 );
foreach( $user->addresses as $address ) // calls getIterator() of ModelRelation
{
    // whatever
}

事情是这样的;如果对象图在某一点上变得非常复杂和深度嵌套,这可能会变得非常慢,因为映射器都必须查询它们自己的数据(假设您正在使用数据库进行持久化)。当我在CMS中使用此模式获取嵌套的Pages对象(任意深度的子页面)时,我就遇到过这种情况。

它可能会与一些缓存机制进行调整,以大大加快速度。