【发布时间】:2010-10-10 06:21:57
【问题描述】:
我有一个包含表示层、业务层、DAL 和业务对象层的 n 层应用程序。分离对象和写在对象上的操作打破了面向对象的封装概念。
【问题讨论】:
标签: design-patterns oop architecture n-tier-architecture
我有一个包含表示层、业务层、DAL 和业务对象层的 n 层应用程序。分离对象和写在对象上的操作打破了面向对象的封装概念。
【问题讨论】:
标签: design-patterns oop architecture n-tier-architecture
没有。考虑一下“封装”的含义:类的实现细节隐藏在类的接口(消息或方法)后面。
事实上,您可以直接从 OO 原则和帕纳斯定律推导出 n 层架构:模块应该封装可能发生变化的内容。表示层封装了创建“可见”界面的细节;中间层是业务本身的模型;以及后端访问持久数据存储的详细信息。
【讨论】:
以这个例子为例,取自this article:
public class Position
{
public double distance( Position position )
{
// calculate and return distance...
}
public double heading( Position position )
{
// calculate and return heading...
}
public double latitude;
public double longitude;
}
根据同一篇文章,这是一个很好的封装示例,因为数据和对该数据执行的操作是捆绑在一起的。请注意,此处的封装并不能保证数据隐藏或保护。
相比之下,Steve McConnell 在 Code Complete(第 2 版,第 6.2 节,良好封装)中辩称封装被破坏,因为成员数据被暴露。
在您的情况下,如果您的数据对象和操作它们的对象是分开的但没有公共字段,则根据第一个定义,封装被破坏,但不一定在第二种情况下。所以我们有两种截然不同的观点。一个人说数据隐藏不是封装的一部分,另一个消息来源说数据隐藏是封装的重要组成部分。
数据隐藏可以看作是信息隐藏的一部分,该原则规定您应该隐藏复杂的设计决策和变更来源。普遍的共识似乎是,封装被视为信息隐藏的一种表现形式,包括数据隐藏。
或者,正如Wikipedia 所说:
经常使用术语封装 与信息互换 隐藏。并非所有人都同意 两者之间的区别; 人们可能会认为信息隐藏是 是原理和封装 作为技术。一个软件模块 通过封装隐藏信息 信息到模块或其他 呈现界面的构造。
但是...本段后面的参考文献与我取第一个示例的文章是同一篇文章,所以即使是维基百科也在这里混淆了一些东西。此外,这里使用“区别”一词似乎是错误的。
最后不得不说这话有点蹩脚,但封装和信息隐藏的术语已经超载,所以这一切都取决于来源。在您的情况下,我会坚持将封装定义为“将实现细节隐藏在抽象后面”。因此,您不一定要破坏封装。
【讨论】:
这是一个非常棒的点AP!
在某些情况下,我同意将单个操作分解为多个对象确实会对封装产生不利影响。事实上,我认为这是我在许多 Web 架构中看到的一个大问题。
另一方面,某些东西对分解很有用,例如向数据库发送查询的对象等。
我认为,在许多情况下,有一个论据可以更好地“封装”网页,以便更多的功能包含在单个对象或更少的对象中。因此,这是一种更加“以页面为中心”的方法,而不是将逻辑分散到大量类中。
我认为对于我们设计的每个系统,我们总是要问自己一个至关重要的问题——要抽象多少?具体要多少?如何有效地将系统分解为类。
【讨论】:
两者不一样。架构更多地是指模块(类组)。封装是指隐藏类的内部工作。如果您的系统设计得足够好,您可以同时拥有两者。
您可能需要注意的是明确定义责任分离。只要您清楚每个模块/层的用途,并具体了解每个类的作用(以及该类的每个方法的作用),那么您应该能够有一个好的设计。
我只是在这里推测,但您在设计模块接口时可能对facade design pattern 感兴趣。
关于您下面的评论,逻辑绝对应该在员工类本身中。我会质疑为业务对象设置单独层的逻辑。定义像“Employee”这样的类通常是一种诱惑,因为它们对现实世界的对象或概念进行建模。但是,您定义类的动机不应该是“为真实世界对象建模”。
你应该定义你的模块的目的(为什么你有一个业务逻辑层?包含所有的业务逻辑。)然后把你需要的东西放在那里。如果“Employee”类是需要计算业务逻辑的类,那么它应该进入业务逻辑类。如果不是,那么它应该放在您的业务对象类中(无论是什么用途)。如果它需要做两件不同的事情,因此可以适合两层,那么考虑将它分成两个类 - 请记住,您的对象不需要对现实世界的事物进行建模。 Robert C Martin 建议定义你的类,这样类的边界就是目的的边界。
【讨论】: