【问题标题】:Polymorphism and tree structre多态性和树结构
【发布时间】:2016-08-06 18:17:40
【问题描述】:

之前问过以下问题后:rest api and polymorphism

我想知道我的整个数据库架构和类结构可能是错误的。 目前的结构是:

  • PERSON BASE ABSTRACT CLASS 包含姓名和年龄
  • CHILD EXTENDS PERSON 包含最喜欢的电视节目
  • PARENT EXTENDS PERSON 包含孩子列表
  • GRANDPARENT EXTENDS PERSON 包含父母列表

在 db 中,每个子类型的表组织在表中

我想也许重构类,因为子类型没有添加任何有价值的字段,除了子/父母的列表,只有一个类包含所有 字段:

 @Entity
 class Person{
  @ManyToOne 
  private Person parent;   //parent==null is a grandparent

  @OneToMany
  private List<Person> children;
}

但是,这样做的问题是,在我的业务逻辑中,父子和祖父确实有不同的行为,这样很难区分 此外,孩子的父母不能是祖父母。

另一个问题是,假设我保留当前的类结构
分离类并没有真正帮助我,因为我使用的是服务层
而且我无法确定我需要哪个服务类,例如:

class PeopleController extends Controller { 
    public Result savePerson() {
      Person p = objectMapper.readValue.. // deseralizes to correct subtype
      // saving logic is different for each subtype, hence I need to 
      // find correct repository for this subtype but I don't want to use 
      // instance of or switch case, but to use polymorphism, but can't think 

一种不实现我不想要的活动记录的方式 } }

【问题讨论】:

  • 祖父母不是……只是父母的父母吗?那么,为什么你认为你需要特别对待他们呢?以及如何对待祖父母的父母?含义:对你的抽象非常精确;不要依赖从不同角度容易看到的属性。而且,你确定一个人只有一个父母吗?有趣的想法,当您考虑时。也许,在我们这个时代,假设有父亲-父母-人和母亲-父母-人已经不合适了。但最有可能的是:大多数人的生活中恰好有两个父母。
  • 换句话说,来自“领域驱动设计”方面:你真的应该花更多的时间思考你的领域;以及您想要使用的模型。
  • @GhostCat ,在我的项目域中,他们应该有一个父母,这很好.. “祖父母不......只是父母的父母吗?那么,你为什么认为你需要特别对待他们?” - 因为在我的业务逻辑中,他们在很多方法上都有不同的行为,而且孩子的父母不能是祖父母。

标签: java database hibernate oop polymorphism


【解决方案1】:

type的确定可以完全基于状态来确定,不需要使用多态,因为你的问题一开始并没有提供任何真正的使用依据。您定义为收集的属性似乎对所有Persons 都是合理的。

  1. Parent 是任何包含非空子集的人。
  2. 所有Person 实例构成一个Child,但对于本练习,它可能暗示任何包含子集的人。
  3. GrandParent 是任何属于Parent 的人,但还要求其子集中的至少一个实例为Parent

考虑到这一点,我们可以考虑如下重构数据模型。

@Entity
public class Person {
  @Id
  @GeneratedValue
  private Integer id;
  private String name;
  private Integer age;
  // any person can have this, not just children imo :)
  private String favoriteShow;

  @OneToMany(mappedBy = "parent")
  private Set<Person> children;

  @ManyToOne
  private Person parent;

  @Transient
  public boolean isChild() {
    return children == null || children.isEmpty();
  }

  @Transient
  public boolean isParent() {
    return !isChild();
  }

  @Transient
  public boolean isGrandParent() {
    return isParent() 
         && children.stream().filter( Person::isParent ).count() > 0;
  }
}

即使使用这种方法,您的逻辑也可以基于布尔瞬态方法检查进行分支。当然有一些方法可以优化这些方法,但我不会太担心,因为这些检查已经在 J​​DK8 上得到了很好的优化。

不过,这里的好处是您可以简单地拥有一个与控制器交互的 Person 服务,并且很可能拥有一个 Person 存储库,因为数据作为一种类型对齐。

我意识到您的保存逻辑因类型而异,但我的问题是它真的必须不同吗?也许更深入的推理为什么需要拆分这些可以帮助我们提供更多背景信息。

【讨论】:

  • 您还会注意到,我选择不将GrandParent 基于空父引用,而是基于GrandParent 是否有任何具有子的子,应用现实世界的域语义。
  • 似乎是一种合理的方式,但我担心如果 else/switch 案例太多,我的代码会变得臃肿。正如我所说,在大多数方法中,孩子/父母/祖父母都有不同的行为,我宁愿通过覆盖应用程序服务的方法(每个子类型的方法)来解决它,而不是使用 if else /switch case 的一种方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-29
  • 2012-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-22
相关资源
最近更新 更多