[英]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.