【问题标题】:Is POJO anti Object Orientation (OO)?POJO 是反面向对象 (OO) 的吗?
【发布时间】:2015-09-04 15:52:08
【问题描述】:

在大多数定义中,POJO(普通旧 Java 对象)是具有属性的对象以及 getter/setter。有些人将这些 DTO 命名为。所以基本上它们就是我喜欢称之为“数据类”的东西。无论名称是什么,我都知道有这类对象只有 getter/setter。这些通常用于开发多层 MVC Web 应用程序 - 这些对象将通过不同的层传递。

也许我的问题有点固执己见——但这是否是纯粹的 OOP? 我决心找出答案,即什么是对的,什么是错的?

...因为,我从小就知道 OOP 就是通过交互将现实生活场景/情况建模成对象。对象本身封装了数据和作用于该数据的行为。 - 在我看来,只有 getter/setter 的对象不符合这个定义。

另外,数据类很糟糕(代码味道)——这就是我在大学里学到的东西。

【问题讨论】:

  • 那么,除了数据类,您更愿意使用什么?地图?
  • Erm,取决于我猜的问题。但我认为地图是完全可以接受的。没有?
  • 用一个对象来表示意图和封装信息。
  • 不,具有具体类型的数据对象肯定比任何地方的地图都更 OO。
  • 查看 Java EE 的历史,以更好地理解 POJO 一词的意义。在早期版本的 J2EE 中,每个 bean 需要一个接口和三个类,每个类都实现一个特定的平台接口! POJO 旨在简化所有无关的样板代码。另外,请注意许多behavioral design patterns 专门将行为与数据分离:例如,您不希望每个数据对象都知道 XML 和 JSON 以及其他数据格式。

标签: java oop object pojo


【解决方案1】:

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();

基本考虑面向对象编程!大多数程序员都忘记了!

【讨论】:

  • 在 OOPs 中,string name; 是一个状态信息,该状态信息应该发送给子级 - ManWoman 类,但在典型的 POJO 中,此字段将标记为 @987654329 @ with public getter & setter & 这就是它与 OOP 以及大量重复代码冲突的地方。
【解决方案2】:

您可能将 POJO 与 Beans 混为一谈,并且存在显着差异。

  • bean 是一个类,它对其 getter/setter 的命名有严格的约定(get___ 用于任何非布尔值,is___ 用于布尔值)。 POJO 没有。
  • bean 中有一个无参数的构造函数,它可以实现Serializable。 POJO 可能也不行。
  • bean 的字段必须是私有的。 POJO 的字段可能不是。

现在,问题是:如果做得好,POJO 可以正确封装数据并以 OO 方式运行。您将在特定对象上进行操作,而不是暴露其内部结构让您不得不摆弄(例如,如果您只有一张地图可以使用;它会略微更好如果您将该地图封装在一个对象中)。

它不会真正以 OO 方式运行的时候是 POJO 承担了超出其应有的责任。

【讨论】:

    【解决方案3】:

    我意识到这个问题已经存在一年了。但是我觉得其他答案没有解决 POJO 的主要问题。

    回答您的问题:“POJO 是否反面向对象 (OO)?”。

    不是和是的。

    简单的答案说明

    不,因为 OO 是关于您需要的建模概念。 POJO 正是这样做的。它将信息位捆绑到一个对象中。

    是的,因为在 OO 中使用了一些 POJO 违反的原则。比如tell don't ask-原理。

    详细解答说明

    POJO 只是数据容器。你不会告诉 POJO 做任何事情。它只是一些信息的表示。因此,您询问它的当前状态。这意味着你违反了tell dont ask

    然而,这在某一时刻是不可避免的。就像函数式编程语言引入 IO 定义来指定函数不是“纯”的一样。你将不得不走出你整洁的小 OO-bubble 程序来询问事情......呃......外面......

    包装你的 POJO

    这就是我将 POJO 包装在模型中的原因(命名取决于您喜欢什么,我称它们为模型)。您可以通过扩展(就像克劳斯在他的回答中所做的那样)或组合(我更喜欢)来做到这一点。哪一个会让你心动。

    POJO 定义了一个真实/抽象的数据源(您并不总是对真实的东西建模)。

    实现告诉不要问

    然后将其传递给负责操作 POJO 的 DataModel。

    它通过被“告知”操作 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 中并带有您可以调用的函数,可以让您做一些更清洁的事情。同时将操作或类型检查的责任转移到课堂之外。

    POJOTypeDataModel model = new POJOTypeDataModel(POJO);
    

    然后上面的 If-elseif-else-statement 因为要简单得多。

    model.doSomething();
    

    正如我上面提到的,您将不得不在某一时刻询问“POJO.type”。 当我第一次需要数据源时,我会这样做。数据源调用返回 POJO。您检查它的类型并返回一个 DataModel 而不是 POJO。

    需要强调的是,您只需检查一次 POJO 的类型!在您检索/创建 POJO 并将其包装的那一刻。

    如果 POJO 没有被包装或没有专门键入,您将不得不更多地询问 POJO 的类型。这使使用 POJO 的类的原始意图变得混乱。 它还去掉了很多 if 语句。

    此外,您还鼓励使用抽象而不是特定类型。

    简而言之

    执行上述操作可以帮助您:

    1. 把外面放在外面。因为 POJO 通常是数据源。
    2. 尽可能坚持tell don't ask
    3. 鼓励使用抽象而不是具体类型(不能比 POJO 更具体)。
    4. 编写更简洁(您不会到处进行类型检查)并且更易于理解代码(众所周知,人们不擅长理解大量程序代码)。
    5. 最终,包装可以帮助您避免更改代码。因为您可能在代码中的任何地方都对 POJO 进行类型检查(参见 if 示例)。现在您只需在 DataModel 上调用一个函数,它就知道该做什么了。

    回到简单的答案

    您无法避免向 POJO 询问信息。然而,它非常适合对数据源进行建模。

    因此,包装它并使用抽象来调用 DataModel 可以限制违反某些核心 OO 原则,并且可以使代码更整洁。

    【讨论】:

      【解决方案4】:

      没有纯粹的 OOP 这样的东西。多年来,人们使用 OO 的方式发生了变化。

      我更倾向于为我的类型添加行为 (https://en.wikipedia.org/wiki/Domain-driven_design)。如果您将类型视为数据容器 (POJO),则需要将该行为放在某处。通常称为 xxxService。这是一个额外的间接级别,只是为了做一些可能是那个“POJO”的核心业务的事情。

      在编写更多代码后,您可能最终会在其他地方需要此功能。现在您需要将其提取到一些您可以共享的助手中。这意味着您将有 2 个服务调用对您的 POJO 起作用的相同方法。添加了另一个间接级别。

      但这是一种方法。我参与过很多项目,很多开发人员对 setter/getter 感到更舒服。如果你的团队对 setter/getter 感觉更舒服,为什么不呢。 它不会扼杀这个项目,但你必须每天都看到它;)

      【讨论】:

        【解决方案5】:

        当您说数据类时,我假设您在谈论与 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
        }
        

        如果不好,为什么人们偶尔会使用 struct 而不是 C++ 中的类?

        【讨论】:

        • 我自己不是 C++ 开发人员,我自己是 Java 开发人员。但我把你的 cmets 带上船。肯定是受欢迎的。很好的思考点。
        • @SoftwareDeveloper 当然。此外,当我们谈论 OO 时,它会提醒我们诸如封装、继承和多态之类的事情。即使它是一个只有 getter 和 setter 的类,它仍然遵守上述 3 个属性,并且仍然符合 OOP 概念。
        猜你喜欢
        • 2011-03-26
        • 2010-09-11
        • 1970-01-01
        • 1970-01-01
        • 2014-02-10
        • 1970-01-01
        • 1970-01-01
        • 2016-12-06
        • 1970-01-01
        相关资源
        最近更新 更多