业务对象 - 容器还是功能? [关闭]
Posted
技术标签:
【中文标题】业务对象 - 容器还是功能? [关闭]【英文标题】:Business Objects - Containers or functional? [closed] 【发布时间】:2010-12-20 03:20:05 【问题描述】:在我工作的地方,我们在这个主题上反复讨论了很多次,并且正在寻找健全性检查。这里的问题是:业务对象应该是数据容器(更像是 DTO)还是应该包含可以在该对象上执行某些功能的逻辑。
示例 - 以一个客户对象为例,它可能包含一些常见属性(名称、ID 等),该客户对象是否还应该包含函数(保存、计算等)?
一条推理说,将对象与功能分开(单一责任主体),并将功能放在业务逻辑层或对象中。
另一种推理方式是,不,如果我有一个客户对象,我只想调用 Customer.Save 并完成它。如果我正在消费对象,为什么我需要知道如何拯救客户?
我们最近的两个项目已经将对象与功能分开,但在一个新项目上再次引发了争论。哪个更有意义?
编辑
这些结果与我们的辩论非常相似。一票或另一票完全改变了方向。还有人想加他们的 2 美分吗?
编辑
尽管答案样本很小,但似乎大多数人认为业务对象中的功能只要简单就可以接受,但持久性最好放在单独的类/层中。我们会试一试。谢谢大家的意见...
【问题讨论】:
【参考方案1】:我们多年来一直使用 Rocky Lhotka 的 CSLA 框架,并且喜欢它的设计方式。在该框架中,所有功能都包含在对象中。虽然我可以看到分离逻辑的价值,但我认为我们不会很快放弃这种理念。
【讨论】:
好点。我也在几个项目中使用了 CSLA。我从来没有想过要把它包括在我们的辩论中。【参考方案2】:在 MVC 架构中,
我们可以说模型包含业务对象吗?
【讨论】:
【参考方案3】:业务对象,正如它们的名字一样,显然应该包含它们自己的业务逻辑,业务逻辑在领域之间的动态是在服务层中的。
另一方面,BO 是否可以是数据容器(DTO?)组合和方法;意思是 BO 是纯功能性的?这样可以避免 BO 和 DTO 之间的所有转换。
【讨论】:
【参考方案4】:无论平台或语言如何,答案都是一样的。这个问题的关键是一个对象是否应该能够自治,或者将任何给定的行为分散在具有更多专注责任的对象之间是否更好。
每个班级的答案可能不同。我们最终得到了一个范围,我们可以根据责任密度来放置类。
(Level of responsibility for behavior)
Autonomy - - - - - - - - - - - - - - - - - - - Dependence
High
C - <<GOD object>> <<Spaghetti code>>
l -
a -
s -
s -
-
s -
i -
z -
e - <<Template>> <<Framework>>
low
假设您喜欢让班级自己执行所有行为,或者尽可能多地执行。从该图的左侧开始,当您使您的类更加自治时,除非您不断地重构它以使其更通用,否则类的大小将会增长。这会产生一个模板。如果不进行重构,则倾向于使该类变得更加“神似”,因为如果它需要某些行为,它就有一种方法。领域和方法的数量不断增长,很快就变得难以管理和不可避免。由于这个类已经做了这么多,编码人员宁愿增加这个怪物,也不愿试图把它拆开并切断 Gordian 结。
图表右侧的类在很大程度上依赖于其他类。如果依赖级别很高但单个类很小,那就是框架的标志;每个类都做的不多,需要很多依赖的类来完成一些功能。另一方面,高度依赖的类也有大量的代码,这表明该类充满了Spaghetti。
这个问题的关键是确定您在图表上感觉更舒服的位置。无论如何,除非应用某种组织原则,否则各个类最终会散布在图表上,这就是您可以实现 Template 或 Framework 的结果的方式。 p>
刚刚写完,我想说班级规模和组织程度之间存在相关性。 Robert C. Martin(或“Uncle Bob”)在他关于 Design Principles and Design Patterns 的非常详尽的论文中涵盖了类似的包依赖关系。 JDepend 是第 26 页图表背后想法的实现,是对 static analysis tools 的补充,例如 Checkstyle 和 PMD。
【讨论】:
+1 不错的细分。这将为我们的辩论增添很多内容。谢谢。 图表工作需要多长时间?非常好的答案...网站上最好的问答之一。 @Stephanie:这需要大量的尝试和错误。这个答案是我在 Meta 上提交功能请求以在 Markdown 中支持表的原因之一。 meta.stackexchange.com/questions/16356/… 这个页面应该是这样的。太多的业务对象应该是关于封装由该对象建模的业务实体的数据和相关行为。可以这样想:面向对象编程的主要原则之一是封装数据和该数据上的相关行为。
持久性不是建模对象的行为。如果业务对象不了解持久性,我发现开发进展会更顺利。如果业务对象没有特别绑定到底层管道,那么开发新代码和对新代码进行单元测试会更快、更顺畅。这是因为我可以模拟这些方面,而不必费力地进入数据库等。我的单元测试将执行得更快(如果您在每个构建中运行数千个自动化测试,这是一个巨大的优势)而且我压力会更小,因为我不会因为数据库连接问题而导致测试失败(如果您经常离线或远程工作并且不能总是访问您的数据库并且哦,顺便说一下,这些方面(数据库连接等)应该在其他地方进行测试!)。
另一条推理说,不,如果我有一个客户对象,我只想调用
Customer.Save
并完成它。如果我正在消费对象,为什么我需要知道如何拯救客户?
知道Customer
有一个Save
方法就已经知道如何保存一个客户对象。通过将该逻辑嵌入到业务对象中,您并没有避免该问题。相反,您使代码库耦合更紧密,因此更难维护和测试。将持久化对象的责任推给其他人。
【讨论】:
似乎这里的大多数答案都采取了非常单方面的方法。只有一种语言,因此您可以将“代码”对象与业务对象混为一谈。我是否天真地采取更广泛的观点,我的业务对象的方法没有在 BO 本身的设计中实现,而是在每个寻求使用该 BO 的解决方案中实现? @Stephanie Page:它与语言的关系不大,而与潜在的模式有关。当对象行为的焦点从域跨越到环境时,问题就出现了。如果将对象保存到数据库中,我们是否需要在每个域/业务对象中包含数据库逻辑,即连接字符串和字段/列映射或 SQL 语句?除非将数据库逻辑分解到自己的层中,否则这种方法很快就会变得难以管理。很少有语言可以处理这种情况并防止它成为臃肿或冗余代码的来源。 我喜欢这样:从领域跨越到环境……这是一个很好的比喻。【参考方案6】:业务对象可以具有业务功能。
持久性不是业务功能,而是技术实现。
长话短说:
-
保存/更新/删除/查找等 - 远离持久层中的业务对象。
CalculateSalary、ApplyDiscount 等是与业务相关的方法,可以是:
-
业务对象的方法(因此 BO 是实体的自包含表示)或;
独立服务实现特定功能(因此 BO 更像 DTO)。
至于第2点。 我应该提一下,方法 2.1 往往会使 BO 过于臃肿并违反 SRP。而 2.2 引入了更多的维护复杂性。
我通常平衡在 2.1 和 2.2 之间,以便我将与数据相关的琐碎事情放入 Business Objects 并为更复杂的场景创建服务(如果有超过 4 行代码- 使其成为一项服务)。
这将业务对象的范式转变为更多的数据传输对象。
但这一切都使项目更易于开发、测试和维护。
【讨论】:
我将 2.1 和 2.2 拆分为所需的东西。需要业务对象或仅需要相同功能组的相关对象的功能 - 进入对象(CalculateAge on Person)。使用更多不相关对象的进程(计算折扣,当它考虑到居住、交付等时)是一个单独的服务,很可能甚至是一个单独的模块/程序集。非常复杂的逻辑也是一个单独的类(PaymentPlanCalculator on a credit),但使用作为扩展方法公开的 C#(在类上使用,但从技术上讲不是在它上面)。 感谢您让我了解 SRP。这不是会增加类的数量,使知道在哪里进行维护变得更加困难吗?我知道,应该是一个全新的问题。【参考方案7】:对象是状态和行为。如果一个对象有合理的行为(例如,从一个人的出生日期计算年龄,或者一张发票的总税额),一定要添加它。只不过是 DTO 的业务对象被称为“贫血域模型”。我不认为这是设计要求。
坚持是一种特殊的行为。我所说的“明智”是商业行为。业务对象不需要知道它是持久的。我想说的是,DAO 可以将持久性与业务行为分开。我没有把“保存”放在“明智”的类别中。
【讨论】:
只是澄清......你的想法是计算和其他简单的功能应该驻留在对象中,但持久性(即数据库事务)应该驻留在其他地方。我有这个权利吗? 是的,这就是我要说的。当然,这不是唯一的方法。有些人喜欢让对象持久化。我通常不会。 我们一直在讨论业务对象中的所有功能或无。这是我们从未想过的一个很好的妥协方案。 @DuffyMo,诚实的问题:让我们设计一个 BO,它只是一个 DTO atm。您的意思是,在我添加行为之前,它不会成为 BO?但是我的 BO 应该是一个企业“事物”,任何具有多种语言的系统都可以使用……我会选择哪种语言来启用行为?还是根据业务规则(有点像接口)提供要由项目团队实现的存根来定义行为是否足够? 嗨斯蒂芬妮,当然只是我的意见。如果您想将您的 DTO 称为业务对象,请成为我的客人。我只是在争辩说,没有行为的对象对我来说就像 C 结构,几乎不配称为“对象”。所有状态,没有行为。有些人将所有业务行为分离为服务,并让它们操作简单的 DTO。这是一种更实用的做事风格,更少面向对象。但它们都是标签,只是我的看法。【参考方案8】:我认为,让业务对象知道如何“处理”自己,然后不得不将这个负担放在系统的其他地方,这更有意义。在您的示例中,对我来说,处理如何“保存”客户数据的最合乎逻辑的地方是 Customer 对象。
这可能是因为我认为数据库是“数据容器”,所以我赞成“业务对象”是保护数据容器不被直接访问并执行标准“业务规则”的更高级别如何访问/操作这些数据。
【讨论】:
我会说 Customer 对象应该知道如何创建“可保存”表示(例如,哪些属性需要稍后保存和恢复); 实际上保持该表示(例如在数据库中)的细节应该完全传递给另一个类。 由于 BO 可以被十几个具有不同语言和数据存储的系统使用,我很乐意看到这项工作。以上是关于业务对象 - 容器还是功能? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
在哪里存储持久容器(或任何全局对象)? AppDelegate 还是场景委托?