POJO 是反面向对象(OO)的吗?
Is POJO anti Object Orientation (OO)?
在大多数定义中,POJO(普通旧 Java 对象)是具有属性的对象,只有 getters/setters。有些人将这些命名为 DTO。所以基本上我喜欢称它们为 'Data classes'。无论名称是什么,我都知道有这些类型的对象,其中只有 getters/setters。这些通常用于开发多层 MVC web 应用程序 - 这些对象将通过不同的层传递。
也许我的问题有点自以为是——但这是否是纯粹的 OOP?
我决心找出这个问题的答案,即什么是对的,什么是错的?
...因为,我从小就知道 OOP 就是通过交互将现实生活 scenarios/situations 建模为对象。对象本身封装了数据和作用于该数据的行为。
- 在我看来,只有 getters/setters 的对象不符合这个定义。
另外,数据 类 很糟糕(代码味道)——这就是我在大学里学到的东西。
您可能将 POJO 与 Beans 混淆了一点,但两者之间存在显着差异。
- bean 是一个 class,它对其 getters/setters 的命名有很强的约定(
get___
用于任何非布尔值,is___
用于布尔值) . POJO 没有。
- 一个 bean 中有一个无参数的构造函数,它可以实现
Serializable
。 POJO 可能也做不到。
- bean 的字段必须是私有的。 POJO 的字段可能不是。
现在,问题来了:如果做得好,POJO 可以正确地封装数据并以面向对象的方式运行。您将对特定对象进行操作,而不是暴露其内部结构,以便您必须 fiddle 与(例如,如果您只有一个地图可以使用;它将是 如果将该地图封装在一个对象中,稍微 会更好)。
它不会真正以 OO 方式运行的时候是 POJO 承担了比它应该承担的更多的责任。
当您说数据 类 时,我假设您说的是与 C 和 C++ 中的 struct
类似的结构,其中它仅包含数据。我个人不认为数据 类 不好,也不是代码味道。
数据类 允许您将属于同一实体的多个属性组合在一起。例如:
class Monster{
String name;
int hp;
int damage;
}
通过以上你可以在参数中传递整个怪物对象:
public void attack(Monster m){
//Attack monster
}
而不是:
public void attack (String name, int hp, int damage){
//Attack monster
}
如果不好,为什么人们偶尔会在 C++ 中使用 struct
而不是 类?
没有纯 OOP 这样的东西。多年来,人们使用 OO 的方式发生了变化。
我更倾向于为我的类型添加行为 (https://en.wikipedia.org/wiki/Domain-driven_design)。如果将类型视为数据容器 (POJO),则需要将该行为放在某处。通常是一个叫做 xxxService 的东西。这是一个额外的间接级别,只是为了做一些可能是 "POJO" 的核心业务的事情。
在编写更多代码后,您可能最终会在其他地方需要此功能。现在你需要将它提取到一些你可以共享的助手。这意味着您将有类似 2 个服务调用对您的 POJO 起作用的相同方法。添加了另一个间接级别。
但这是一种方法。我从事过很多项目,很多开发人员对 setter/getter 感到更舒服。如果你的团队对 setters/getters 感觉更舒服,为什么不呢。
它不会终止项目,但您必须每天查看它 ;)
POJO 和 XXService 是个坏主意!任何动作(方法)都属于一个对象。
好吧,看个例子,就是人家的简单动作:
Class People {
string name;
}
Class PeopleService{
public void pee(People people){
System.out.print(people.name + " relax...");
}
}
啊哈,现在我们需要扩展它...,我们知道男人和女人之间的一些区别
class Man extends People{}
class Woman extends People{}
如果只有一项服务,你应该写:
Class PeopleService {
public void pee(People people){
if(people instanceof Man)//if ... and if ...
System.out.println("stand up");
if(people instanceof Woman)
System.out.println("sit down");
System.out.print(people.name + " relax...");
}
}
如果您延长服务时间:
Class ManService extends PeopleService {
@Override
public void pee(People people){
if(people instanceof Man)
System.out.println("stand up");
super.pee();
}
}
Class WomanService extends PeopleService {
@Override
public void pee(People people){
if(people instanceof woman) //you must check it
System.out.println("sit down");
super.pee();
}
}
//maybe wrong when anyone call these service
...
People p = new Woman();
...
manService.pee(p);//you can't stop it!
比我们能写一个 serviceFactory...,太疯狂了!
但是 OOP 代码,People 是域对象,它是完美的!
Class People {
String name;
public void pee(){
System.out.print(name + " relax...");
}
}
Class Man extends People {
@Override
public void pee() {
System.out.println("stand up");
super.pee();
}
}
Class Woman extends People {
@Override
public void pee() {
System.out.println("sit down");
super.pee();
}
}
//we call it now, don't care about details
...
People p1 = new Man();
People p2 = new Woman();
p1.pee();
p2.pee();
基本想到了OOP!大多数程序员都忘记了!
我意识到这个问题已经有一年了。但是我觉得其他答案没有解决 POJO 的问题。
回答您的问题:"Is POJO anti Object Orientation (OO)?"。
不,是。
简单解答
不,因为 OO 是关于您需要的建模概念。 POJO 正是这样做的。它将信息位捆绑到一个对象中。
是的,因为 OO 中使用了一些 POJO 违反的原则。比如tell don't ask
-原则。
详细答案解释
POJO 只是 data-containers。您不会告诉 POJO 做任何事情。它只是一些信息的表示。因此,您询问它的当前状态。这意味着您违反了 tell dont ask
.
然而,这在某一时刻是不可避免的。就像函数式编程语言引入 IO 定义来指定函数不是 "pure" 一样。你将不得不走出你整洁的小 OO-bubble 程序去问事情.. urhg.. 外面..
包装您的 POJO
这就是我将 POJO 包装在模型中的原因(命名取决于您喜欢什么,我称它们为模型)。您可以通过扩展(就像克劳斯在他的回答中所做的那样)或组合(我更喜欢)来做到这一点。哪一个让你爱不释手。
POJO 定义了一个 real-life/abstract data-source(你并不总是建模 real-life 东西)。
实行告诉不问
然后将其传递给负责操作 POJO 的 DataModel。
它通过 "told" 来操纵 POJO 来做到这一点。我这样做的原因是因为有时 POJO 包含类型信息。
因此,直接操作 POJO 通常会导致某些 Class 不得不检查 POJO 的类型。
经常做这样的事情:
if(POJO.type == "something"){
this.doSmthWithPOJO();
}
现在您可能会得到大量不同类型的 POJO。在那种情况下,你可能会得到这样的结果:
if(POJO.type == "something"){
this.doSomethingWithPOJO(POJO);
}
else if (POJO.type == "something else")
{
this.doSomethingElseWithPOJO(POJO);
}
else if( POJO.type == "something else entirely")
{
this.doSomethingElseEntirelyWithPOJO(POJO);
}
else{
this.print("This is starting to get ugly...");
}
您这样做是因为 POJO 中的类型信息意味着您必须做一些稍微不同的事情。将 POJO 放在具有您可以调用的函数的 DataModel 中,可以让您做一些更简洁的事情。在将操作或类型检查的责任转移到 class.
之外
POJOTypeDataModel model = new POJOTypeDataModel(POJO);
那么上面的If-elseif-else-statement就简单多了
model.doSomething();
正如我上面提到的,您必须在某一时刻询问 "POJO.type"。
当我第一次需要 data-source 时,我会这样做。 data-source 调用 return POJO。您检查它的类型和 return DataModel 而不是 POJO。
重要的是要强调您只需检查一次 POJO 的类型!现在您 retrieve/create POJO 并将其包装起来。
如果 POJO 没有包装或没有专门类型化,您将不得不更多地询问 POJO 的类型。这扰乱了使用 POJO 的 class 的初衷。
它还摆脱了很多 if-statements.
此外,您还鼓励使用抽象而不是特定类型。
简而言之
执行以上操作可以帮助您:
- 保持外面。由于 POJO 通常是 data-source.
- 尽量坚持
tell don't ask
。
- 鼓励使用抽象而不是具体类型(不能比 POJO 更具体)。
- 写得更干净(你不会 type-check 无处不在)和更容易理解的代码(众所周知,人们不善于理解大块的过程代码)。
- 最终,包装可以帮助您避免更改代码。因为您可能 type-check 在您的代码中到处使用 POJO(参见 if 示例)。现在您只需在 DataModel 上调用一个函数,它就知道该做什么了。
回到简单答案
您无法避免向 POJO 询问信息。然而,它非常适合为 data-source 建模。
因此包装它并使用抽象来调用 DataModel 限制了对某些核心的违反 OO-principles 并且它使代码更整洁。
在大多数定义中,POJO(普通旧 Java 对象)是具有属性的对象,只有 getters/setters。有些人将这些命名为 DTO。所以基本上我喜欢称它们为 'Data classes'。无论名称是什么,我都知道有这些类型的对象,其中只有 getters/setters。这些通常用于开发多层 MVC web 应用程序 - 这些对象将通过不同的层传递。
也许我的问题有点自以为是——但这是否是纯粹的 OOP? 我决心找出这个问题的答案,即什么是对的,什么是错的?
...因为,我从小就知道 OOP 就是通过交互将现实生活 scenarios/situations 建模为对象。对象本身封装了数据和作用于该数据的行为。 - 在我看来,只有 getters/setters 的对象不符合这个定义。
另外,数据 类 很糟糕(代码味道)——这就是我在大学里学到的东西。
您可能将 POJO 与 Beans 混淆了一点,但两者之间存在显着差异。
- bean 是一个 class,它对其 getters/setters 的命名有很强的约定(
get___
用于任何非布尔值,is___
用于布尔值) . POJO 没有。 - 一个 bean 中有一个无参数的构造函数,它可以实现
Serializable
。 POJO 可能也做不到。 - bean 的字段必须是私有的。 POJO 的字段可能不是。
现在,问题来了:如果做得好,POJO 可以正确地封装数据并以面向对象的方式运行。您将对特定对象进行操作,而不是暴露其内部结构,以便您必须 fiddle 与(例如,如果您只有一个地图可以使用;它将是 如果将该地图封装在一个对象中,稍微 会更好)。
它不会真正以 OO 方式运行的时候是 POJO 承担了比它应该承担的更多的责任。
当您说数据 类 时,我假设您说的是与 C 和 C++ 中的 struct
类似的结构,其中它仅包含数据。我个人不认为数据 类 不好,也不是代码味道。
数据类 允许您将属于同一实体的多个属性组合在一起。例如:
class Monster{
String name;
int hp;
int damage;
}
通过以上你可以在参数中传递整个怪物对象:
public void attack(Monster m){
//Attack monster
}
而不是:
public void attack (String name, int hp, int damage){
//Attack monster
}
如果不好,为什么人们偶尔会在 C++ 中使用 struct
而不是 类?
没有纯 OOP 这样的东西。多年来,人们使用 OO 的方式发生了变化。
我更倾向于为我的类型添加行为 (https://en.wikipedia.org/wiki/Domain-driven_design)。如果将类型视为数据容器 (POJO),则需要将该行为放在某处。通常是一个叫做 xxxService 的东西。这是一个额外的间接级别,只是为了做一些可能是 "POJO" 的核心业务的事情。
在编写更多代码后,您可能最终会在其他地方需要此功能。现在你需要将它提取到一些你可以共享的助手。这意味着您将有类似 2 个服务调用对您的 POJO 起作用的相同方法。添加了另一个间接级别。
但这是一种方法。我从事过很多项目,很多开发人员对 setter/getter 感到更舒服。如果你的团队对 setters/getters 感觉更舒服,为什么不呢。 它不会终止项目,但您必须每天查看它 ;)
POJO 和 XXService 是个坏主意!任何动作(方法)都属于一个对象。
好吧,看个例子,就是人家的简单动作:
Class People {
string name;
}
Class PeopleService{
public void pee(People people){
System.out.print(people.name + " relax...");
}
}
啊哈,现在我们需要扩展它...,我们知道男人和女人之间的一些区别
class Man extends People{}
class Woman extends People{}
如果只有一项服务,你应该写:
Class PeopleService {
public void pee(People people){
if(people instanceof Man)//if ... and if ...
System.out.println("stand up");
if(people instanceof Woman)
System.out.println("sit down");
System.out.print(people.name + " relax...");
}
}
如果您延长服务时间:
Class ManService extends PeopleService {
@Override
public void pee(People people){
if(people instanceof Man)
System.out.println("stand up");
super.pee();
}
}
Class WomanService extends PeopleService {
@Override
public void pee(People people){
if(people instanceof woman) //you must check it
System.out.println("sit down");
super.pee();
}
}
//maybe wrong when anyone call these service
...
People p = new Woman();
...
manService.pee(p);//you can't stop it!
比我们能写一个 serviceFactory...,太疯狂了! 但是 OOP 代码,People 是域对象,它是完美的!
Class People {
String name;
public void pee(){
System.out.print(name + " relax...");
}
}
Class Man extends People {
@Override
public void pee() {
System.out.println("stand up");
super.pee();
}
}
Class Woman extends People {
@Override
public void pee() {
System.out.println("sit down");
super.pee();
}
}
//we call it now, don't care about details
...
People p1 = new Man();
People p2 = new Woman();
p1.pee();
p2.pee();
基本想到了OOP!大多数程序员都忘记了!
我意识到这个问题已经有一年了。但是我觉得其他答案没有解决 POJO 的问题。
回答您的问题:"Is POJO anti Object Orientation (OO)?"。
不,是。
简单解答
不,因为 OO 是关于您需要的建模概念。 POJO 正是这样做的。它将信息位捆绑到一个对象中。
是的,因为 OO 中使用了一些 POJO 违反的原则。比如tell don't ask
-原则。
详细答案解释
POJO 只是 data-containers。您不会告诉 POJO 做任何事情。它只是一些信息的表示。因此,您询问它的当前状态。这意味着您违反了 tell dont ask
.
然而,这在某一时刻是不可避免的。就像函数式编程语言引入 IO 定义来指定函数不是 "pure" 一样。你将不得不走出你整洁的小 OO-bubble 程序去问事情.. urhg.. 外面..
包装您的 POJO
这就是我将 POJO 包装在模型中的原因(命名取决于您喜欢什么,我称它们为模型)。您可以通过扩展(就像克劳斯在他的回答中所做的那样)或组合(我更喜欢)来做到这一点。哪一个让你爱不释手。
POJO 定义了一个 real-life/abstract data-source(你并不总是建模 real-life 东西)。
实行告诉不问
然后将其传递给负责操作 POJO 的 DataModel。
它通过 "told" 来操纵 POJO 来做到这一点。我这样做的原因是因为有时 POJO 包含类型信息。 因此,直接操作 POJO 通常会导致某些 Class 不得不检查 POJO 的类型。
经常做这样的事情:
if(POJO.type == "something"){
this.doSmthWithPOJO();
}
现在您可能会得到大量不同类型的 POJO。在那种情况下,你可能会得到这样的结果:
if(POJO.type == "something"){
this.doSomethingWithPOJO(POJO);
}
else if (POJO.type == "something else")
{
this.doSomethingElseWithPOJO(POJO);
}
else if( POJO.type == "something else entirely")
{
this.doSomethingElseEntirelyWithPOJO(POJO);
}
else{
this.print("This is starting to get ugly...");
}
您这样做是因为 POJO 中的类型信息意味着您必须做一些稍微不同的事情。将 POJO 放在具有您可以调用的函数的 DataModel 中,可以让您做一些更简洁的事情。在将操作或类型检查的责任转移到 class.
之外POJOTypeDataModel model = new POJOTypeDataModel(POJO);
那么上面的If-elseif-else-statement就简单多了
model.doSomething();
正如我上面提到的,您必须在某一时刻询问 "POJO.type"。 当我第一次需要 data-source 时,我会这样做。 data-source 调用 return POJO。您检查它的类型和 return DataModel 而不是 POJO。
重要的是要强调您只需检查一次 POJO 的类型!现在您 retrieve/create POJO 并将其包装起来。
如果 POJO 没有包装或没有专门类型化,您将不得不更多地询问 POJO 的类型。这扰乱了使用 POJO 的 class 的初衷。 它还摆脱了很多 if-statements.
此外,您还鼓励使用抽象而不是特定类型。
简而言之
执行以上操作可以帮助您:
- 保持外面。由于 POJO 通常是 data-source.
- 尽量坚持
tell don't ask
。 - 鼓励使用抽象而不是具体类型(不能比 POJO 更具体)。
- 写得更干净(你不会 type-check 无处不在)和更容易理解的代码(众所周知,人们不善于理解大块的过程代码)。
- 最终,包装可以帮助您避免更改代码。因为您可能 type-check 在您的代码中到处使用 POJO(参见 if 示例)。现在您只需在 DataModel 上调用一个函数,它就知道该做什么了。
回到简单答案
您无法避免向 POJO 询问信息。然而,它非常适合为 data-source 建模。
因此包装它并使用抽象来调用 DataModel 限制了对某些核心的违反 OO-principles 并且它使代码更整洁。