I'm sorry if the phrasing of the question is a bit unclear but let me try to clarify below. (If anyone can word it better, feel free to edit)
I have a Map
instance variable, groups
, which is defined as follows with the given type parameters:
Map<Group, Map<Username, GroupMember>> groups
Within the same enclosing class, I have a public method, addGroup(Group group)
, which is supposed to put a new entry into the groups
map. The Group
object contains all the relevant information for the entry.
The conundrum here stems from the fact that adding a new element to groups
would require that I specify a concrete object for Map<Username, GroupMember>
value within addGroup()
, thus internally coupling the abstraction with an implementation--clearly in violation of the dependency inversion principle.
My question is then how do I avert this problem? I hesitate delegating the responsibility of defining the object to the caller (i.e. defining the method as addGroup(Group group, Map<Username, GroupMember> groupMembers)
for instance) as it chips away at abstraction, may be inconsistent with the constructor injected implementation; and in my opinion, introduces unnecessary coupling with the class implementation details. Perhaps I should reconsider my design.
EDIT
Here's where the problem is coming from:
public class Foo {
private final Map<Group, Map<Username, GroupMember>> groups;
//...
public void addGroup(Group group) {
if (exists(group))
throw new IllegalOperationException("Group with same name already exists");
//Declaring a new HashMap within the class (i.e. defining the implementation within a class member)
groups.put(group, new HashMap<Username, GroupMember>(Map evaluation based on group object)));
}
//...
}
Here's what I'm hesitant of doing:
//Passing Map<Username, GroupMember> object decreases abstraction and exposes implementation details
public void addGroup(Group group, Map<Username, GroupMember> groupMembers) {
if (exists(group))
throw new IllegalOperationException("Group with same name already exists");
groups.put(group, groupMembers));
}
Here's part of the definition for Group
to further add context;
public class Group {
private final String name;
private final String description;
private final Instant dateCreated;
private final Collection<GroupMember> members;
//... Constructor and getters for the fields
}
Map<Username, GroupMember>
. So, simply implementingnew HashMap
internally is fine. If it ever needs to change, the callers will never know nor be impacted by the change. The Dependency Inversion violation is really that you're usingaddGroup(Group)
instead ofaddGroup(IGroup)
. Whether or not that violation is worth changing is up for debate - it's not a rule I advocate following 100%.computeIfAbsent()
Foo
in charge of associating usernames to group members? Shouldn't that be a responsibility of aGroup
?