繁体   English   中英

如何根据ORM框架的需要,在getter和setter中协调最佳的面向对象编程实践?

[英]How to conciliate the best Object Oriented Programming practices in getters and setters with the needs of ORMs frameworks?

在我工作过的许多系统中,代表模型的类都是POJO,我们将它们的字段映射到列(在关系数据库中)或属性(在某些NoSQL数据库中)。 因此,在许多ORM中,必须具有访问器方法来从数据库中获取数据或从中获取数据。 但是,面向对象编程的最佳实践表明,我们不应该公开对象的内部结构。 相反,我们必须公开更改对象内部状态的操作e保持该对象的状态一致性

让我们举个例子。 假设我们有一个Client类。 此类具有ID,客户名称及其最后更改日期。 我们无法直接更改此数据,但我们希望保留它们。 如果要修改客户的名称,还必须更改ID和最后更改日期。

ORM需要以下getter和setter方法,因此我们具有:

@Entity
public class Client {

  @Id
  private Long id;

  @Index
  private String name;

  private Date lastChange;

  public Long getId() {
    return this.id;
  }

  public String getName() {
    return this.name;
  }

  public Date lastChange() {
    return this.lastChange;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public void setString(String name) {
    this.name = name;
  }

  public void setLastChange() {
    this.lastChange = latChange;
  }

}

这样,除了ORM之外,任何人都可以更改ID和最后更改日期,从而对系统的其余部分造成不良影响。

相反,如果我们要遵守“面向对象”规则,则应具有以下内容:

@Entity
public class Client {

  @Id
  private Long id;

  @Index
  private String name;

  private Date lastChange;

  public Client(Long id, String name) {
    this.id = id;
    this.name = name;
    this.lastChange = new Date();
  }

  public Long getId() {
    return this.id;
  }

  public String getName() {
    return this.name;
  }

  public void changeName(String name) {
    this.name = name;
    this.id = newIdFromClient();
    this.lastChange = new Date();
  }

  private Long getNewIdFromClient() {
    return (new Random()).nextLong();
  }

}

因此,我的问题是:我们如何将面向对象编程的最佳实践与ORM框架中的getter和setter的需求协调起来?

无关:

在我工作过的许多系统中,代表模型的类是POJO,

POJO是Java类,它不实现接口或扩展其他(抽象)类以满足框架的需求。 您最可能指的是数据传输对象 (DTO)或bean (通常是POJO)。

主题:

但是,面向对象编程的最佳实践表明,我们不应该公开对象的内部结构。 相反,我们必须公开更改对象内部状态的操作e保持该对象的状态一致性

DTO / bean是纯数据结构,不是通过OOP的对象。 不幸的是,在Java中,我们必须使用相同的类概念来创建它们(与例如Kotlin不同,在Kotlin中,我们具有用于数据类的特殊关键字)。

因此,数据结构可以公开其内部结构是可以的,因为这就是它们的全部意义所在。

但是,在构建可用于DTO / bean的业务逻辑的类中,您应严格遵循严格的信息隐藏原则,并避免使用getter / setter。

有人认为您不应该使用ORM 阅读该文章。 即使您不一定同意,它也会挑战您的成见。

您说对了,ORM依赖于getter和setter的java“ bean”模式,这会破坏封装并强制对象进行不必要的可变。

在我看来,使自己受益于两个世界的最佳方法是使用两个不同的类。 以下仅是伪代码来说明一般想法:

@Entity
class ORMFoo
{
   private int bar;

   ORMFoo(int bar){...}

   void setBar() {...}
   int  getBar() {...}
   Foo  getImmutable() { return new Foo(bar); }
}

class Foo
{
   private final int bar;
   Foo(final int bar) {...}
   save(ORMManager manager) { manager.update(new ORMFoo(bar)); }
}

在您的大多数应用程序中都使用后者。 仅将前者用作传输对象。

像JDXA这样的ORM(免责声明:我是Android的JDXA ORM的架构师)可以通过使用非标准的替代setter方法(例如,具有以下签名)来设置属性值来解决此问题:

void setAttribValue(String attribName, Object Value)

因此,其他开发人员将看不到并(滥用)“标准” bean设置方法,但是ORM可以采用此替代方法以供自己使用。 例如,在您的情况下,JDXA可以使用以下类定义:

public class Client {

  private Long id;
  private String name;
  private Date lastChange;
  private int something;

  public Client() {
  }

  public Client(Long id, String name) {
    this.id = id;
    this.name = name;
    this.lastChange = new Date();
  }

  public Long getId() {
    return this.id;
  }

  public String getName() {
    return this.name;
  }

  public Date getLastChange() {
    return this.lastChange;
  }

  public int getSomething() {
     return something;
  }

  // May be OK to expose this bean style setter method
  public void setSomething(int something) {
    this.something = something;
  }

  // JDXA ORM will automatically use this method to set values of those 
  // attributes for which it cannot find a 'regular' setter method. 
  public void setAttribValue(String attribName, Object value) {
    switch (attribName) {
        case "id":
           this.id = (Long) value;
           break;
        case "name":
           this.name = (String) value;
           break;
        case "lastChange":
           this.lastChange = (Date) value;
           break;
        default:
           throw new IllegalArgumentException("Invalid attribute name: " + attribName);
      }
      return;
  }

  public void changeName(String name) {
    this.name = name;
    this.id = getNewIdFromClient();
    this.lastChange = new Date();
  }

  private Long getNewIdFromClient() {
    return (new Random()).nextLong();
  }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM