2010-10-24 51 views
18

我目前正在设计我的类时遇到循环依赖关系问题。自从我阅读了关于Anemic Domain Model(我一直在做的事情)之后,我一直试图摆脱创建只是“getters和setter桶”的域对象,并返回到我的OO根。面向对象设计和循环依赖关系

但是,下面的问题是我遇到了很多,我不知道我应该如何解决它。

假设我们有一个团队类,即有很多玩家。无论这是什么运动都无所谓:)一个球队可以添加和移除球员,就像球员可以离开球队并加入另一个球员一样。

因此,我们有团队,其中有球员名单:

public class Team { 

    private List<Player> players; 

    // snip. 

    public void removePlayer(Player player) { 
     players.remove(player); 
     // Do other admin work when a player leaves 
    } 
} 

然后我们有播放器,它具有对团队参考:

public class Player { 
    private Team team; 

    public void leaveTeam() { 
     team = null; 
     // Do some more player stuff... 
    } 
} 

我们可以假定这两个方法(移除和离开)具有领域特定的逻辑,当团队移除球员并且球员离开球队时需要运行领域特定的逻辑。因此,我首先想到的是,当团队踢的球员,removePlayer(...)也应调用player.leaveTeam()方法...

那么是什么,如果球员是驱动离开 - 应该leaveTeam()方法调用team.removePlayer(this)?不是没有创建一个无限循环!

在过去,我只是让这些对象“哑”POJOs和有一个服务层做的工作。但即使是现在我还是留下了这个问题:为了避免循环依赖,服务层仍具有链接它一起 - 即

public class SomeService { 

    public void leave(Player player, Team team) { 

     team.removePlayer(player); 
     player.leaveTeam(); 

    } 

} 

我是不是在复杂吗?也许我错过了一些明显的设计缺陷。任何反馈将不胜感激。


感谢所有的回复。我接受Grodriguez的解决方案,因为它是最明显的(不能相信它不会发生在我身上)并且易于实现。然而,DecaniBass确实是一个很好的观点。在我所描述的情况下,球员可能会离开球队(并且知道他是否在球队中)以及推动球队移动的球队。但我同意你的观点,我不喜欢这个过程中有两个“切入点”的想法。再次感谢。

回答

14

您可以通过添加警卫来检查团队是否仍然有玩家/玩家仍然在队伍中,从而打破循环依赖。例如:

Team类:

public void removePlayer(Player player) { 
    if (players.contains(player)) 
    { 
     players.remove(player); 
     player.leaveTeam(); 
     // Do other admin work when a player leaves 
    } 
} 

Player类:

public void leaveTeam() { 
    if (team != null) 
    { 
     team.removePlayer(this); 
     team = null; 
     // Do some more player stuff.. 
    } 
} 
+2

可能只是我而已,但我喜欢尽可能保守地使用if ... else。我注意到它使得代码不易维护 – 2010-10-24 09:50:17

+4

players.remove()将在集合被更改时返回true;不需要执行.contains()。 – KarlP 2010-10-24 10:12:49

+0

@KarlP:我知道,但我认为明确的检查会使逻辑更加清晰。 – Grodriguez 2010-10-24 10:26:52

1
public void removePlayer(Player player) { 
    if (players.contains(player)) { 
     players.remove(player); 
     player.leaveTeam(); 
    } 
} 

同上leaveTeam

2

想法是做不同的方法域相关的东西不打电话给对方,但确实域相关为自己的对象的东西,即团队的方法是否适合团队和球员的方法是否适合球员

public class Team { 

    private List<Player> players; 

    public void removePlayer(Player player) { 
     removePlayerFromTeam(player); 
     player.removeFromTeam(); 
    } 
    public void removePlayerFromTeam(Player player) { 
     players.remove(player); 
     //domain stuff 
    } 
} 

public class Player { 
    private Team team; 

    public void removeFromTeam() { 
     team = null; 
     //domain stuff 
    } 
    public void leaveTeam() { 
     team.removePlayerFromTeam(this); 
     removeFromTeam(); 
    } 

} 
+1

当你打电话时,'leaveTeam()'方法会抛出一个NPE 'team = null'后设置'team.removePlayerFromTeam()'。 – Grodriguez 2010-10-24 08:06:37

+0

同样在这个解决方案中,调用'player.leaveTeam()'实际上并不会从团队对象中的玩家列表中移除玩家。同样,调用'team.removePlayer()'也不会在播放器对象中将'team' var设置为'null'。 – Grodriguez 2010-10-24 08:08:30

+1

在这个设计中,我认为包含域特定代码的方法应该是包私有的,而不是公开的。但这绝对是我想要的路线。 – Waldheinz 2010-10-24 09:25:12

7

本,

我会开始问一个球员是否可以(合乎逻辑地,合法地)从球队中解脱出来。我会说玩家对象不知道他在哪个队(!),他是一个团队的一部分。因此,请删除Player#leaveTeam(),并通过Team#removePlayer()方法进行所有团队更改。

在你只有一个球员,需要从它的团队中取出的情况下,那么你可能会对团队public static Team findTeam(Player player) ...

我知道这是不是一个Player#leaveTeam()方法不太令人满意的,自然的静态查找方法,但以我的经验,你仍然可以拥有一个有意义的领域模型。

2的方式引用(父 - >儿童和儿童安全>家长)往往与其他事物充满,说垃圾收集,维持“参照完整性”等

设计是一种妥协!