【问题标题】:Looking for the best way to make a web framework more OO on the server side寻找让 Web 框架在服务器端更 OO 的最佳方式
【发布时间】:2012-10-10 20:05:14
【问题描述】:

Web 框架似乎总是偏离良好的 OO 代码(您将代码和数据组合在一个对象中,考虑向该对象发送消息等)

主要问题似乎是bean模式的存在。您使用 bean 模式(现在通常称为 POJO,因为人们似乎不了解其中的区别)与数据库、Web 服务和大多数其他事物进行交互。

所以,不,你会被这个没有代码的 setter 和 getter 困住——所以你倾向于添加一堆——本质上是静态函数来操作这些东西。 OO 代码的几乎所有优点都没有了。

我看到了三个正在考虑的解决方案,但想知道其中任何一个是否存在严重的缺点。

1) 使用 POJO 模式而不是 bean 模式。这意味着从您的 POJO 中消除 setter 和 getter(这样您就可以拥有封装/数据安全),而是添加业务逻辑方法。这似乎是最有意义的——事实上我认为这就是为什么像 hibernate 这样的库从需要 Bean 转为允许 POJO 的原因,但似乎每个人仍然使用 bean 模式。

2) 使用业务逻辑类扩展您的 Bean,该业务逻辑类使用 bean 的字段作为存储。这看起来很不稳定且有问题,但作为一种迁移方式很容易放入现有代码库中......

3) 用业务逻辑对象包装 Bean 模式对象。大多数情况下,如果#1 真的有问题,我认为这可能是必要的。

我最想知道的是为什么我从未见过 #1 使用过。我真的很想听听任何使用 Pojo 模式(有业务逻辑,没有 setter 和 getter)并遇到问题(或者让它工作得很好)的人...

【问题讨论】:

    标签: java hibernate javabeans pojo oop


    【解决方案1】:

    我会将我的评论更改为答案。我基本上不同意你的开场白

    Web 框架似乎总是偏离良好的 OO 代码。

    Java EE 或 Spring/JPA 模型中没有任何东西“远离良好的 OO 代码”。像 hibernate 这样的 JPA 提供程序允许 POJO 的事实并不意味着您不能使用在 bean 中定义业务逻辑的富域模型。对于 Java Web 应用程序,我已经看到它以两种方式完成——丰富的域模型,或者在服务层中具有业务逻辑的贫乏域模型,这不是非 OO 或糟糕的设计。

    我认为您的意思可能是很多应用程序都没有使用正确的设计和/或正确的 OO 编写。 我会同意的。但这与框架无关......

    所以您提出的解决方案基本上都说“使用良好的 OO 原则正确设计您的应用程序”。选项 1 是最简单的。

    【讨论】:

    • 你是对的,我完全说错了。我的意思是,当有人使用典型的 Web 框架(包括 bean 和事件处理程序往往是函数而不是方法的事实)时,我所看到的实践。并不是你不能写出好的 OO,只是我工作过的大多数地方,那些本来会写 OO 风格的人根本没有——主要是因为你必须从“bean”中获取和设置" 而不是仅仅让 bean 为你做点什么。
    【解决方案2】:

    将(大部分)业务逻辑保留在数据对象之外是有充分理由的,但最佳设计将其中的某些部分包括在内。要做到这一点并不容易,尤其是在蓝领团队环境中,我猜这是所有 Java 开发团队的 95%。

    我喜欢将一些简单、独立、可广泛重用的逻辑片段放入我的数据对象中;本质上与该数据耦合的代码,而不考虑任何特定的业务请求。例如一些转换、验证等。

    就 getter 和 setter 而言,我不惜一切代价避免使用它们。您可以使用公共最终字段,或者只是普通的公共字段。 getter+setter 组合为您提供与公共字段一样多的封装:零。

    就 ORM 解决方案而言,我严格使用 Hibernate 作为 SQL 的便利,而不是作为持久状态管理器。映射类只是作为 XML 映射的替代品。它们甚至从未在读取操作中实例化。对于插入,我单独保存每个对象,它与单个数据库行一对一映射,没有传递持久性。这基本上意味着我的 Hibernate 模型对象只是一种查看数据库行的 Java 方式(加上方便的连接列规范——没有那些冗余连接条件的 HQL 看起来要好得多)。

    【讨论】:

    • 我真的很喜欢这里的第一段。我并不热衷于直接公开属性而不是访问器方法,它们都鼓励人们从你的对象中获取数据,而不是让你的对象为你做一些事情(判断你是否正在编写好的 OO 代码的好方法。 ..),但确实很好。
    • 在早期的项目中,我尝试制作一个包含所有业务逻辑的“肌肉”领域模型。对象是 ORM 映射的。结果是一团糟,在部署多年后出现了一些细微的错误。此外,大多数业务逻辑操作并不仅仅与一个实体相关联,因此将它们放入实体对象中是没有意义的。
    • 我还发现了 ORM 不支付的困难方式。没有什么可以保护您免受 SQL 的影响——而且没有什么不应该,真的。如果您想要性能,您唯一的希望是分别为每个业务服务自定义 SQL(或 HQL),准确获取所需的数据量,并在一次提取中全部获取(如果可能的话)。
    • 优秀的 cmets,谢谢——正是我想要的!我认为您关于仅将与对象高度相关的轻量级内容放入域的建议也很好(我见过的大多数代码甚至都没有这样做——它往往完全遵循 bean 模式)。只是出于好奇,“肌肉”领域模型造成了什么样的错误?
    • 在其他答案之一中实际上提到了重要的一点——从外部看起来像普通 getter 的方法实际上在内部执行了额外的数据库查询。我提到的最糟糕的问题是,只有在访问每个集合成员时才对该成员进行延迟计算。连续使用了 5 年,直到用户滚动到某个项目才在 GUI 列表中看到它,然后因为当时数据库会话已经关闭而中断。
    【解决方案3】:

    我对您在这里争论的确切内容感到困惑,正如您所说的那样,人们都倾向于将 bean 称为“POJO”,然后您建议不要使用 bean,而是使用“POJO”。但要回应这一点:

    所以,不,你会被这个没有代码的 setter 和 getter 困住——所以你倾向于添加一堆——本质上是静态函数来操作这些东西。 OO 代码的几乎所有优点都没有了。

    我不同意这一点 - 如果您正在编写静态函数来操作您的对象,那么您的设计就有问题。

    典型的常见/“最佳”实践是将业务逻辑包装在知道如何操作数据的类的“服务层”中(在您的 bean、POJO、域对象中表示,无论您想如何称呼它们) ) 以响应应用程序的规则。

    没有人阻止你让你的“服务层”是面向对象设计的,有子类和继承等等。

    通常,您不会看到有人构建一个应用程序,其中保存数据的Employee 类还包含对Employee 执行操作的方法和逻辑,因为人们倾向于分离数据(表示为Employee 类)来自逻辑。而且实际上,一旦您开始在Employee 类中使用对员工实例进行操作的方法,那么您需要让Employee 类能够调用存储层本身,现在您使用的类表示查询结果的 Hibernate(Employee 类)也引用了 Hibernate 代码来加载自身,这变得一团糟。

    我认为您看到的“问题”只是您对这些类型的应用程序常见的设计理念存在分歧。大多数人都希望将数据的表示方式与封装应用程序逻辑的代码区分开来。

    【讨论】:

    • 结合代码和数据几乎是 OO 的定义,所以是的,你的最后一段很好地总结了它。我看到的问题是我不同意——我碰巧喜欢 OO 代码,并想通过重新组合代码和数据将其重新引入这些情况。
    • 好吧,也许我在最后一点上太强了。我认为将逻辑和数据结合起来通常是有意义的,但是在诸如具有多个层的 webapp 之类的东西中,您要小心设计的 OO 部分也不关心诸如“如何持久化这个对象”之类的事情或“如何检索我的数据”——这就是事情变得过于复杂的地方。
    【解决方案4】:

    根据最佳实践,业务逻辑应与表示逻辑分开。大多数 Web 框架都围绕数据的呈现提供了很好的功能。任何涉及数据检索/操作的业务逻辑都应该写在Business Layer中,这是presentation layerdata layer之间的附加层。 POJO 对象充当Business LayerPresentation Layer 之间的数据载体。 POJO 是非常好的候选对象,因为它们是非常轻的对象,具有作为访问器方法的属性。

    休息由你决定。如果您想使用其中包含业务逻辑的复杂 bean 对象,我认为任何 Web 框架都不会阻止您这样做。

    希望这会有所帮助。

    【讨论】:

      【解决方案5】:

      也许您可以查看其他 Knockout(一个 JavaScript 框架)使用的模型-视图-视图模型设计模式。这里引入了 View Model 作为 Model 和 View 之间的一层,提供了将视图与模型分离的可能性。

      在此处了解更多信息:http://knockoutjs.com/documentation/observables.html ..这里http://en.wikipedia.org/wiki/Model_View_ViewModel

      【讨论】:

        【解决方案6】:

        “我最想知道的是为什么我从未见过 #1 被使用过。”关注点分离、单一职责原则(可能是最重要的)、可测试性等。“一切都耦合到一切”的模型很方便,但客观上很糟糕。 (这并不是说方便的 API 表面不好,但它们应该构建在经过良好分解的、可测试的代码之上,而不是代替它。)

        例如,当您在业务逻辑层上添加“哑” DTO 层时,其中每个视图都映射到包含创建视图所需的所有内容的 bean,您就可以摆脱诸如必须保持会话打开的问题获取关联的属性,或者让 ORM 急切地获取它们。 (前者还意味着有 N+1 个选择问题,这会增加数据库往返次数。)

        如果每个请求都由专用服务层上的单个方法调用提供服务,它还可以更轻松地跟踪事务范围。然后,此调用是对数据的单个逻辑操作,这使得设置事务处理变得微不足道。

        最后但同样重要的是,这种方法可以生成更易于理解和自行更改的小类。例如。如果您需要更改计算平均绩点的算法,请从GradePointAverageCalculator 开始并从那里导航,而不是您需要查看的代码范围是“Student.java,第 1340 到 1486 行,然后是 1502 到 1689, ……”

        【讨论】:

          猜你喜欢
          • 2018-06-20
          • 1970-01-01
          • 1970-01-01
          • 2019-11-13
          • 1970-01-01
          • 1970-01-01
          • 2016-11-09
          • 2015-01-19
          • 2012-01-17
          相关资源
          最近更新 更多