2015-02-09 136 views
4

A User可以属于不同的Group s。并且(根据定义),Group可以具有不同的成员。因此,以下类:摆脱循环依赖

class User { 
    List<Group> groups; 

    public User() { 
     // initialize groups here 
    } 
} 

class Group { 
    List<User> members; 

    public Group() { 
     // initialize members here 
    } 
} 

的问题是,当我创建一个User,它需要创造一个Group,当我创建Group,它需要重新创建User。我如何摆脱这种无限递归?


这里就是我想要做的事:

我有一组User S,Group S和映射他们都存储在数据库中的关系。

每当有人需要使用User时,他们创建一个new User(<someId>)。这为他们提供了一个新的User对象,它是实际从数据库中提取数据的类的代理(如RealUser)。在内部,我保留了RealUser的缓存,这样我就不会从数据库中两次获取每个User。同样,Group将是RealGroup类的代理。

这就是原因为什么我在User的内部创建了Group,反之亦然。他们都代表真正的课程。

+6

这是一个问题域问题。为什么用户需要创建一个组*和*反之亦然? – 2015-02-09 22:15:43

+0

换句话说,问题是他们需要彼此创造,而不是创造彼此是有问题的,这是。 – keyser 2015-02-09 22:17:08

+2

这个结构从一开始就有缺陷。这意味着一个用户由多个组组成,每个组由多个用户组成(包括初始用户和其他许多复制的用户)。你需要的是一组用户,一组组,一个关系“是成员”和一个关系“包括”。 – 2015-02-09 22:26:21

回答

2

一个简单的选择是存储这两个类之外的用户和组之间的关系。

例如,您可以使用java.util.Map将用户映射到组,反之亦然。

这是一个可能的表示:

Map<User,Set<Group>> mapUserToGroups = new HashMap<User,Set<Group>>(); 
Map<Group,Set<User>> mapGroupToUsers = new HashMap<Group,Set<User>>(); 

或者,如果用户和组具有唯一的ID,地图可能不是指那些ID。

Map<String,Set<String>> mapUserIDToGroupIDs = new HashMap<String,Set<String>>(); 
Map<String,Set<String>> mapGroupIDToUserIDs = new HashMap<String,Set<String>>(); 
+1

或者,也许只是把这一切都放到数据库 – 2015-02-09 22:18:37

+0

@DavidGrinberg:把东西放在数据库中并不能避免需要在代码中代表它... – 2015-02-09 22:21:16

+0

@OliverCharlesworth没错,但你不再有循环依赖问题。您可以简单地生成组中所有用户的列表,或者用户所属的所有组。 – 2015-02-09 22:22:50

1

一般模式是这样的(不是线程):

class User 
{ 
    private final static Map<String, User> USERS = new HashMap<>(); 

    public static User realize(String userId) 
    { 
    User user = USERS.get(userId); 

    if (user == null) { 
     user = new User(userId); 
     USERS.put(userId, user); 
    } 

    return user; 
    } 

    private final Set<Group> groups = new HashSet<>(); 

    private User(String key) 
    { 
    USERS.put(key, this); 

    Set<String> groupIds = getGroupsForUser(key); 

    for (String id : groupIds) { 
     groups.add(Group.realize(id)); 
    } 

    // etc. initialization 
    } 
} 

class Group 
{ 
    private final static Map<String, Group> GROUPS = new HashMap<>(); 

    public static Group realize(String groupId) 
    { 
    Group group = GROUPS.get(groupId); 

    return group == null ? new Group(groupId) : group; 
    } 

    private final Set<User> members = new HashSet<>(); 

    private Group(String key) 
    { 
    GROUPS.put(key, this); 
    Set<String> memberIds = getUsersForGroup(key); 

    for (String id : memberIds) { 
     members.add(User.realize(id)); 
    } 

    // etc. initialization 
    } 
} 

这里的问题是,你是把对象到地图之前完全实现它们。尤其是在多线程的情况下,这可能会变得很难看。

一种更安全的方法是将Ids用作链接,并根据需要使用相同的方法来实现它们。我可能会赞成后一种方法,因为前者有可能在第一次访问时为整个目录提取数据和初始化对象。下面是User类的一个示例:

class User 
{ 
    private final static Map<String, User> USERS = new HashMap<>(); 

    public static User realize(String userId) 
    { 
    User user = USERS.get(userId); 

    if (user == null) { 
     user = new User(userId); 
     USERS.put(userId, user); 
    } 

    return user; 
    } 

    private final Set<String> groupIds; 

    private User(String key) 
    { 
    USERS.put(key, this); 

    groupIds = getGroupsForUser(key); 

    // etc. initialization 
    } 

    public Set<Group> getGroups() 
    { 
    Set<Group> groups = new HashSet<>(); 

    for (String id : groupIds) { 
     groups.add(Group.realize(id)); 
    } 

    return groups; 
    } 
} 

我已经使用这种类型的设计,广泛在过去十年中,它的快速,可靠和易于维护。