views:

80

answers:

1

Hi! 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?

Thank you!

+11  A: 

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) { ... }
}

Update: enum based example

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

  • I use an 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) { ... }
    }
    
Péter Török
Please could you be more precise? Thank you
xocobola
@xocobola, I added sample code, hope it helps.
Péter Török
Thank you for the edit :) One more think: Could you explain me what do you mean with "being members of a Role enum". Thank you again and sorry to be so dull :S
xocobola
@xocobola, no problem, see my update :-) You may want to check out the [Java Tutorial section on enums](http://download.oracle.com/javase/tutorial/java/javaOO/enum.html) too.
Péter Török
Thaaaaaaaaaaaaaaaaaaaaaaaaaaaaank you very much!
xocobola