PHP OOP:在域模型模式中避免单例/静态方法
我理解依赖注入的重要性及其在单元测试中的作用,这就是为什么以下问题让我暂停:
我努力不使用单例的一个领域是身份映射/工作单元模式(它密切关注域)对象状态)。
//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);
关于如何处理这种情况有什么建议吗?
我不愿意将映射器/数据库访问的实例传递/生成到域对象本身来满足 DI - 同时,避免这会导致域对象内对外部静态方法的大量调用。
尽管我想如果我希望“保存”成为其行为的一部分,那么在其构造中就需要一个这样做的设施。也许这是责任的问题,域对象不应该承担保存的负担。这只是 Active Record 模式中的一个非常巧妙的功能 - 如果能以某种方式实现它就好了。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我所做的(尽管可能不是最好的做法)是为我的类制定明确的命名约定,FI:
user_User
是域对象,user_mapper_User
是它的映射器。在我的父
domainObject
类中,我编写了逻辑来查找它的映射器。然后,您有几个选项可以委托给它,一个明显的选项是使用
domainObject
中的__call()
方法。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 anduser_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 indomainObject
.