Reputation: 2369
I am designing some Java application that has users and teams:
public abstract class AbstractUser()
public class BasicUser extends AbstractUser()
public class AnotherUser extends AbstractUser()
Now I have teams that contain users:
public abstract class AbstractTeam() {
protected List<AbstractUser> members;
}
public class PublicTeam extends AbstractTeam()
Now I need to manage the teams and so each user should have a team status, for example Member, Moderator, Administrator, Owner etc.
So I thought of doing some interfaces:
public Interface TeamRole
public Interface Member extends TeamRole
public Interface Moderator extends TeamRole
and let the AbsractUser class implement TeamRole:
public abstract class AbstractUser implements TeamRole
Now what I want to do: Lets have a controller that handles deleting an entry that a user made:
public boolean deleteEntry(User u, Entry e, requestingUser r) {
}
How can I get a clean representation so I can do something like:
u.getEntries().removeIf(entry -> entry.equals(e));
In a clean way without 'trying' all roles in a if-statement?
Update: This may not sound 'good', but it is just to descrive the problem of checking for the rights when performing operations
Upvotes: 1
Views: 147
Reputation: 43718
I know there's already an answer, but I thought I could share a different idea where we'd use inheritance to express the role hierarchy and the method signature to constrain operations.
Here's the general design idea:
Usage:
public class TeamApplicationService {
public void doSomethingSpecial(final String teamId) {
final Team team = teamRepository.teamOfId(teamId);
final TeamModerator moderator = team.findModerator(currentUserId);
team.doSomethingSpecial(moderator);
}
}
public class Team {
final Map<String, TeamMember> teamMembers;
public Team() {
teamMembers = new HashMap<String, TeamMember>();
}
public void addMember(final TeamMember member) {
teamMembers.put(member.userId(), member);
}
private <T> T findMemberOfRole(final Class<T> clazz, final String userId) {
if (!teamMembers.containsKey(userId)) throw couldNotFindMember();
try {
return (T)clazz.cast(teamMembers.get(userId));
} catch (java.lang.ClassCastException e) {
throw couldNotFindMember();
}
}
private RuntimeException couldNotFindMember() {
return new RuntimeException("Could not find member");
}
public TeamModerator findModerator(final String userId) {
return this.<TeamModerator>findMemberOfRole(TeamModerator.class, userId);
}
public TeamMember findMember(final String userId) {
return this.<TeamMember>findMemberOfRole(TeamMember.class, userId);
}
public void doSomethingSpecial(TeamModerator moderator) {
//Moderator only
}
public void doSomethingGeneral(TeamMember member) {
//Any member
}
}
EDIT: I'm not entirely sure of the requirements, but here's a more complex example based off comments.
Upvotes: 1
Reputation: 954
TeamRole becomes the delete strategy (maybe rename this?). could use more refactoring but i think the general idea would work. If we had more roles we can add more classes and implement how they would handle a delete.
public class Entry{
}
public class User extends AbstractUser{
}
public abstract class AbstractUser {
TeamRole role;
List<Entry> entries;
//self deletes are always ok
public void deleteEntry(Entry e){
this.getEntries().removeIf(entry -> entry.equals(e));
}
//delete depending on requested user role
public void deleteEntry(User requestBy, Entry e){
role.delete(this, e, requestBy);//mod or other poeple with ability to delete.
}
public List<Entry> getEntries() {
return entries;
}
};
public class BasicUser extends AbstractUser {
};
public class AnotherUser extends AbstractUser {
};
public abstract class AbstractTeam {
protected List<AbstractUser> members;
}
public class PublicTeam extends AbstractTeam {
};
public interface TeamRole{
public void delete(AbstractUser user, Entry entry, User requester);
}
public class Member implements TeamRole{
@Override
public void delete(AbstractUser user, Entry entry, User requester) {
if(user==requester) {
user.deleteEntry(entry);
}
}
}
public class Moderator implements TeamRole{
@Override
public void delete(AbstractUser user, Entry entry, User requester) {
user.deleteEntry(entry);
}
}
Upvotes: 1
Reputation: 2793
First off, Hovercraft Full Of Eels is correct in saying that AbstractUser
should compose TeamRole
rather than inherit from it (since it's a 'has a' relationship and not an 'is a' relationship).
Now to the question at hand. I'm not sure if I completely understand what you are asking, however I assume you are asking how to check whether or not an AbstractUser
has permission (or the rights) to perform an action without using an if
or switch
statement to check. If so, then here is a way (among a few that I can think of). Create an Enum
of permission levels each with an ordinal which directly correlates with the permission level. Below is a basic example:
class enum PermissionLevel {
MEMBER(0),
MODERATOR(1)
private final int level;
PermissionLevel(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
}
Now give the TeamRole
interface a method that returns the permission level associated with that role
public Interface TeamRole {
PermissionLevel getPermissionLevel();
...
}
Now when checking if a member has permission all you need to do is an integer comparison on the permission level of the TeamRole
that the AbstractUser
has.
if (user.getTeamRole().getPermissionLevel().getLevel() > PermissionLevel.MEMBER.getLevel()) {
// User has permission to do this action
}
Upvotes: 2