摆脱循环依赖
Getting rid of a circular dependency
一个User
可以属于不同的Group
。并且(根据定义),Group
可以有不同的成员。因此,以下 classes:
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
、Group
和一个映射它们的关系存储在数据库中。
只要有人需要使用 User
,他们就会创建一个 new User(<someId>)
。这为他们提供了一个新的 User
对象,它是实际从数据库中获取数据的 class(例如 RealUser
)的代理。在内部,我保留了 RealUser
的缓存,这样我就不会从数据库中获取每个 User
两次。同样,Group
将是 RealGroup
class.
的代理
这就是 为什么 我要在 User
中创建 Group
的原因,反之亦然。它们都是真实 classes.
的代理
一个简单的选择是将用户和组之间的关系存储在这两个 类 之外。
例如,您可以使用 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>>();
一般模式是这样的(不是线程安全的):
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
}
}
这里的问题是您在对象完全实现之前将其放入地图中。这可能会变得很难看,尤其是在多线程的情况下。
一种更安全的方法是只使用 ID 作为您的链接,并使用相同的方法根据需要实现它们。我可能更喜欢后一种方法,因为前者有可能在第一次访问时为整个目录提取数据和初始化对象。这是用户 class:
的示例
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;
}
}
我在过去十年中广泛使用了这种类型的设计,它快速、可靠且易于维护。
一个User
可以属于不同的Group
。并且(根据定义),Group
可以有不同的成员。因此,以下 classes:
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
、Group
和一个映射它们的关系存储在数据库中。
只要有人需要使用 User
,他们就会创建一个 new User(<someId>)
。这为他们提供了一个新的 User
对象,它是实际从数据库中获取数据的 class(例如 RealUser
)的代理。在内部,我保留了 RealUser
的缓存,这样我就不会从数据库中获取每个 User
两次。同样,Group
将是 RealGroup
class.
这就是 为什么 我要在 User
中创建 Group
的原因,反之亦然。它们都是真实 classes.
一个简单的选择是将用户和组之间的关系存储在这两个 类 之外。
例如,您可以使用 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>>();
一般模式是这样的(不是线程安全的):
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
}
}
这里的问题是您在对象完全实现之前将其放入地图中。这可能会变得很难看,尤其是在多线程的情况下。
一种更安全的方法是只使用 ID 作为您的链接,并使用相同的方法根据需要实现它们。我可能更喜欢后一种方法,因为前者有可能在第一次访问时为整个目录提取数据和初始化对象。这是用户 class:
的示例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;
}
}
我在过去十年中广泛使用了这种类型的设计,它快速、可靠且易于维护。