Reputation: 664
I have an inheritance hierarchy with overlap. The system knows about People that can be Clients, Providers and Agents. A person have to belong to one of these classes but can belong to two or three, i.e. one Person can be a Client and a Provider at the same time.
In the database I think that the problem is solved, one table per class (Person, Client, Provider and Agent table) and a Foreign Key from the Primary Key of the subclasses table to the Primary Key of the superclass table. (Any possible improvement will be welcome :) )
The problem comes in the Java world, I don't know the best way to map this database design to my Java POJOs. I have three possible grotty workarounds:
A unique class called Person, with the union of all the fields in the subclasses. This will need a discriminator field in order to know wich kind of Person is. The problem is that a Person that is not a Client but is a Provider, will have all the Client-related fields set to null.
A unique class called Person with all the common fields to the subclasses and three "DTO-kind" properties that holds the fields related to each subclass. This way we only have one or two fields to null instead of tens
One abstract class for Person and seven subclasses, one per possible combination of the three subclasses i.e. Client, Provider, Agent, ClientProvider, ClientAgent ... ClientProviderAgent. :S (of course, everyone with their corresponding interfaces)
It's a webapp. I use hibernate + spring and GWT for the UI
The question is: which is the best way to solve this problem?
Upvotes: 2
Views: 2201
Reputation: 116266
IMO inheritance is not the best way to model this, I would try composition instead.
You could have a Person class and several Role classes (implementing a common interface, or being members of a Role enum, depending on the context), with each person having one or more Roles attached.
This way you can add new role types easily, and dynamically attach/detach roles to/from a person. (You can also have persons without a role, should the need arise.)
Rough example:
interface Role {
...
}
final class Client implements Role {
...
}
final class Provider implements Role {
...
}
final class Agent implements Role {
...
}
class Person {
List<Role> roles;
public void addRole(Role role) { ... }
public void removeRole(Role role) { ... }
public Role getRoleOfType(Class<? extends Role> roleType) { ... }
}
This is applicable if the role objects have no state, thus you attach the same role instance(s) to every person.
enum Role {
CLIENT,
PROVIDER,
AGENT;
// possible members, constructor etc.
}
The Person class is almost the same as above, except that
EnumSet
instead of a List
, since this is tailored specifically for enums,getRoleOfType()
makes no sense here, so I replaced it with hasRole()
.
class Person {
Set<Role> roles = new EnumSet<Role>();
public void addRole(Role role) { ... }
public void removeRole(Role role) { ... }
public boolean hasRole(Role role) { ... }
}
Upvotes: 12