架构:在不污染 POJO 的情况下操作模型的最佳实践?并且无需到处重复样板代码[关闭]

Posted

技术标签:

【中文标题】架构:在不污染 POJO 的情况下操作模型的最佳实践?并且无需到处重复样板代码[关闭]【英文标题】:Architecture: Best practices for manipulating models without polluting the POJOs? And without repeating boiler-plate code everywhere [closed] 【发布时间】:2012-02-16 23:22:27 【问题描述】:

这是我们经常遇到的问题。必须有一些最佳实践来解决这个问题......

简化问题

在哪里放置操作 POJO 的通用代码的最佳位置?

这样:

POJO 只有属性和 getter/setter 相同的模型操作代码不会“到处”重复 非常清楚哪些类负责操作模型

背景

我们有一个定义我们的域的模式。由此,我们生成了一个由来自 JAXB 的简单对象 (POJO) 组成的“纯”模型。

在使用此模型时,团队中的几位开发人员创建了样板代码来访问和操作模型。它在许多地方都被“洒”了。有些人创建了包装器对象,这些对象是模型实例的子类并添加了功能。其他人创建了外部实用程序类。我希望统一此代码,使其不再“到处乱扔”。 理想情况下,逻辑可以包含在特定类别的对象中,这些对象明确负责模型的一般操作。

示例

让我们以杂货店为例。模型对象由以下内容组成:Products, Aisle, Shelf, Employee, WorkSchedule, Vendor

常见的模型操作包括:findManagerWorkingOnDay(day, schedule), findAisleForProduct(apples), countItemsOnShelf(topShelf), product.isModified(), removeProductFromVendor(apples, vendor)

我们不想用removeProductFromVendor 之类的函数“污染”我们的 Vendor POJO。同样,我们不一定要扩展每个模型对象,只需添加 isModified 属性,这样我们的 GUI 就可以知道“启用/禁用”保存按钮。

我们呢?

总结

一旦模型对象在内存中,谁应该负责操作它——例如,遍历“今天值班的员工”列表并找到一个“经理”?

在这些情况下,数据库调用是多余的,因为我们已经在内存中拥有了我们需要的一切(例如:我们已经查询了 DataStore,我们需要在整个应用程序中处理结果对象)。 理想情况下,任何拥有员工列表等对象的对象都可以使用此代码。


就最佳实践而言,放置静态方法的理想位置在哪里:public static Employee findManager(List<Employee> employeesOnDuty);

这将遍历员工列表 (POJO) 并返回第一个 employee.title.toLowerCase().contains("manager")

如果一个团队要使用这个示例对象模型,有几个人会编写这样的函数。有哪些最佳实践来承担这一责任,以使 POJO 保持“纯粹”并且不会“到处散布”相同的样板代码。

【问题讨论】:

IMO 最好的解决方案是“污染”您的对象,以便它们真正提供行为和封装。这就是 OOP 背后的理念。 对象可以提供行为,是的。但是,在这种情况和许多其他情况下,Pojos(或值对象)提供的“行为”是存储值。而已。高阶对象可以与值对象一起使用,但值对象本身不应包含业务逻辑。我们用于描述对象模型的架构 (XSD) 不包含也不应该包含复杂的逻辑。 【参考方案1】:

鉴于您的 POJO 对象来自供应商,我理解您不愿意向它们添加功能,而且您显然不希望代码随意散布在您的项目周围。

听起来像是一组 Decorator 子类或 Ken 建议的门面的工作。

我喜欢这里的装饰器,因为您只需通过装饰器类的名称来引用您的供应商 pojo,然后将添加的行为(isModified、searchByName 等)全部集中在一个地方。

【讨论】:

我们在某些地方使用了装饰器模式,但我发现很多这些类包含过多的私有或静态方法,这清楚地表明"there is another class in there struggling to get out" 所以这个责任应该属于到另一个班级。 确实,这种方法可能太过分了。防止这种情况发生是您团队中负责处理此类问题的任何人的工作(您,听起来像)。但是确实没有可行的方法来添加,(您的示例) isModified 而不在某处添加一些状态,并且它必须是特定于实例的。 我刚刚再次谈到这个问题,已经是第 5000 次了。在这种情况下,我选择使用装饰器模式,创建像 ProductDecorator 这样的东西来将操作/行为添加到底层 POJO。这一次,我没有制作任何静态方法(这是之前困扰我的部分),因为我打算将这些对象与 Spring 一起使用。我还没有完全确定它的外观,但总的来说,装饰类(或使用外观)似乎是要走的路。我希望我能在你和肯之间分配“正确答案”奖 :) 我一定会支持他...... @gmale 感谢您的跟进和接受。我也认为肯的答案很好,所以我自己给了他一票。【参考方案2】:

据我了解,听起来您的 POJO 模型只是数据(或者您希望以这种方式保留它们)。为什么不创建外观对象,例如 QueryCount 或其他命名的功能分组,隐藏您的操作算法的所有相关机制?

它不会污染 POJO,这听起来像是您想要避免的。

【讨论】:

这是一个有趣的想法,通过功能而不是它们使用的底层对象来创建对象。这对于“计数”来说很容易理解,但对于 findManager(employeesOnDuty) 之类的申请则不太容易。在这些情况下,我想我们可以创建与特定模型对象交互的 Query 子类。 Ken,好方法,但有个问题:你如何像这样实现product.isModified() 没有最优...但是您可以按照Modified.isModified(Product p) 的模式进行一系列调用。然后product.isModified() 将变为Modified.isModified(product)。现实情况是,这些函数只是从 POJO 模型类转移到外观,它变成了一种语法。尽管您失去了使用实现细节来修改状态的能力,但 OP 正在使用提供给他的类,所以这不是一个真正的选择。【参考方案3】:

尽管问题是目前 1 岁,但希望它可能对其他人有所帮助。 因此,您愿意将 POJO 和行为分开,因此这些陈述具有位置: a) POJO 只有属性和 getter/setter, b) 重复使用相同的模型操作代码, c)很清楚哪些类负责操作模型 已得到广泛认可并且正在通过将应用程序拆分为层级来实现,即:

    您的 POJO - 是普通模型,包含字段/属性和 getter/setter; 与模型操作相关的业务逻辑被放置在服务层中 - 在您的情况下,所有与操作有关的事情,例如Employee 可以在 EmployeeService 中分配(因此可以在很多地方重用); 与持久性相关的所有内容都进入 DAO 层,例如EmployeeDAO; 用于在层之间交换数据,例如视图和服务 - 可以使用 DTO(数据传输对象)。

如果将业务逻辑放入模型 bean 中,这就是所谓的领域驱动设计——人们到处问它(我个人不是领域驱动设计的崇拜者)。

希望,这会有所帮助。

【讨论】:

【参考方案4】:

在类似情况下,我们决定将所有“功能”(行为)提升为我们对象模型的一等公民。

所以我们现在有了不同的包,一个只包含 POJO,一个包含函数对象(又名处理器),在这些对象上应用函数。

例如作为 POJO 的 Contract 对象,以及作为处理器的 ContractBilling、ContractFinder、ContractCloser 等。他们中的大多数都根据合同或合同列表进行操作。

我们发现它非常有效,这迫使我们正确记录系统中的所有内容(因为函数已成为对象)。这种方法的架构优势非常显着,即使我们在这个设计中有更多的对象,我们也有更小的类更易于管理、理解和发展。

【讨论】:

谢谢,这很有帮助,尽管它与 Ken 的建议基本相同,(可以说)更好地命名外观对象,这也使得它与建议 decorator pattern 的公认答案相同. +1,因为了解这在您的团队中是如何运作的很有帮助。【参考方案5】:

我猜对于 find* 等操作,它们属于服务类;但是对于“isModified”,服务类将无济于事,它必须在 pojo 本身中,除非当然修改也通过服务类发生。然后,相应的服务类可以在集合中维护此类对象的状态。

【讨论】:

以上是关于架构:在不污染 POJO 的情况下操作模型的最佳实践?并且无需到处重复样板代码[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何在不污染全局命名空间的情况下公开 javascript 对象以进行单元测试

基类有没有办法在不污染 .h 文件的情况下声明受保护的变量?

Jackson:如何在不修改 POJO 的情况下向 JSON 添加自定义属性

如何在不手动转换为 JSON 的情况下使用 Jersey 客户端发布 Pojo?

JAXB XML;如何在不需要创建元素的 pojo 的情况下获取元素的属性?

在不删除数据的情况下更新数据库架构?