摆脱循环依赖

时间:2015-02-09 22:13:52

标签: java design-patterns circular-dependency

User可以属于不同的Group。并且(根据定义),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和一个映射存储在数据库中的关系的关系。

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

这就是为什么我在Group内创建User的原因,反之亦然。它们都是真实课程的代理。

2 个答案:

答案 0 :(得分: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 :(得分: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;
  }
}

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