Перекрывающееся наследование в Java - PullRequest
2 голосов
/ 27 сентября 2010

У меня есть иерархия наследования с перекрытием.Система знает о людях, которые могут быть клиентами, поставщиками и агентами.Человек должен принадлежать к одному из этих классов, но может принадлежать к двум или трем, то есть один Человек может одновременно быть Клиентом и Поставщиком.

В базе данных я думаю, что проблема решена,одна таблица на класс (таблица Person, Client, Provider и Agent) и внешний ключ от первичного ключа таблицы подклассов до первичного ключа таблицы суперкласса.(Любое возможное улучшение будет приветствоваться :))

Проблема возникает в мире Java, я не знаю лучшего способа сопоставить этот дизайн базы данных с моими Java POJO.У меня есть три возможных обходных пути:

  • Уникальный класс Person с объединением всех полей в подклассах.Для этого понадобится поле дискриминатора, чтобы узнать, кто такой человек.Проблема в том, что для Person, который не является Клиентом, но является Провайдером, все связанные с Клиентом поля будут иметь нулевое значение.

  • Уникальный класс с именем Person со всеми общими полями.к подклассам и трем свойствам типа DTO, которые содержат поля, относящиеся к каждому подклассу.Таким образом, у нас есть только одно или два поля для обнуления вместо десятков

  • Один абстрактный класс для Person и семь подклассов, по одному на каждую возможную комбинацию из трех подклассов, например, Client, Provider, Agent,ClientProvider, ClientAgent ... ClientProviderAgent.: S (конечно, каждый со своими соответствующими интерфейсами)

Это веб-приложение.Я использую hibernate + spring и GWT для пользовательского интерфейса

Вопрос в том, как лучше всего решить эту проблему?

1 Ответ

11 голосов
/ 27 сентября 2010

IMO-наследование - не лучший способ смоделировать это, вместо этого я бы попробовал композицию.

У вас может быть класс Person и несколько классов Role (реализующих общий интерфейс или являющихся членами перечисления Role, в зависимости от контекста), с каждым человеком, к которому прикреплена одна или несколько ролей.

Таким образом, вы можете легко добавлять новые типы ролей и динамически присоединять / отсоединять роли от человека. (Вы также можете иметь людей без роли, если возникнет такая необходимость.)

Грубый пример:

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

Обновление: пример на основе enum

Это применимо, если объекты роли не имеют состояния, поэтому вы присоединяете одинаковые экземпляры роли к каждому человеку.

enum Role {
  CLIENT,
  PROVIDER,
  AGENT;
  // possible members, constructor etc.
}

Класс Person почти такой же, как указано выше, за исключением того, что

  • Я использую EnumSet вместо List, так как это специально для перечислений,
  • getRoleOfType() здесь не имеет смысла, поэтому я заменил его на hasRole().

    class Person {
      Set<Role> roles = new EnumSet<Role>();
      public void addRole(Role role) { ... }
      public void removeRole(Role role) { ... }
      public boolean hasRole(Role role) { ... }
    }
    
...