Search code examples
javainheritancemultiple-inheritanceoverlap

Overlapping inheritance in Java


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?


Solution

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