views:

337

answers:

1

I understand the importance of Dependency Injection and its role in Unit testing, which is why the following issue is giving me pause:

One area where I struggle not to use the Singleton is the Identity Map/Unit of Work pattern (Which keeps tabs on Domain Object state).

//Not actual code, but it should demonstrate the point    

class Monitor{//singleton construction omitted for brevity
    static $members = array();//keeps record of all objects
    static $dirty = array();//keeps record of all modified objects
    static $clean = array();//keeps record of all clean objects
}

class Mapper{//queries database, maps values to object fields
    public function find($id){
        if(isset(Monitor::members[$id]){
        return Monitor::members[$id];
    }
    $values = $this->selectStmt($id);
    //field mapping process omitted for brevity
    $Object = new Object($values);
    Monitor::new[$id]=$Object
    return $Object;
}

$User = $UserMapper->find(1);//domain object is registered in Id Map
$User->changePropertyX();//object is marked "dirty" in UoW

// at this point, I can save by passing the Domain Object back to the Mapper
$UserMapper->save($User);//object is marked clean in UoW

//but a nicer API would be something like this
$User->save();

//but if I want to do this - it has to make a call to the mapper/db somehow    
$User->getBlogPosts();

//or else have to generate specific collection/object graphing methods in the mapper
$UserPosts = $UserMapper->getBlogPosts();
$User->setPosts($UserPosts);

Any advice on how you might handle this situation?

I would be loathe to pass/generate instances of the mapper/database access into the Domain Object itself to satisfy DI - At the same time, avoiding that results in lots of calls within the Domain Object to external static methods.

Although I guess if I want "save" to be part of its behaviour then a facility to do so is required in its construction. Perhaps it's a problem with responsibility, the Domain Object shouldn't be burdened with saving. It's just quite a neat feature from the Active Record pattern - it would be nice to implement it in some way.

+1  A: 

What I do, albeit maybe not the best course of action, is to have a clear naming convention for my classes, FI: user_User is the domain object and user_mapper_User is it's mapper.

In my parent domainObject class I code the logic to find it's mapper.

Then you have a couple options to delegate to it, an obvious one would be to use the __call() method in domainObject.

Javier Parra
Sure, that's how I do it too. The problem is this: do you create a NEW instance of the mapper, or do you make a static call to the mapper (given that a User mapper must exist already for there to be a User) which exists outside the User? Cos if you call a new mapper, you probably need to call a new DB handle, plus a reference to the Unit of Work and ID Map... - whereas the external class already has all this set up
sunwukung
I store the DB handle (in my case MDB2) static in the parent mapper class, so no matter how many instances of mapper I have, they all share the same handle.
Javier Parra
Ah, I see now, that's a Monostate class pattern isn't it?
sunwukung
I didn't know it was a pattern. but apparently yeah, it is. Thanks for letting me know :)
Javier Parra