2015-02-09 138 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; 
    } 
} 

我已經使用這種類型的設計,廣泛在過去十年中,它的快速,可靠和易於維護。