服务和门面的角色是否相似?

82
我读得越多,就越感到困惑。
请注意,所有问题都与服务和门面在MVC模式中的适用方式有关。
我的理解是,门面不是一个超级智能的对象,它只是一种暴露简单接口/ API 以执行复杂操作的方法(例如:执行10美元的付款,这是涉及多个操作的复杂操作,但此类复杂性可以通过门面来处理,门面将按特定顺序调用相应的对象等)。
现在,服务是一种执行对多个DAO的调用以获取复杂数据结构的方法(我还不太确定,但这是我目前所理解的)。
问题是,门面和服务之间有什么区别?最终,门面完全可以通过提供简单接口访问多个DAO以执行复杂操作,并且服务似乎也可以做类似的事情。
同样发生在事务中,我了解到服务是启动事务的地方,但我同样觉得它们也可以放置在门面上,毕竟,门面也可能调用多个DAO。
那么哪个堆栈更有意义?
控制器-门面-数据访问对象 控制器-服务-数据访问对象
或者可能是
控制器-门面-数据访问对象,有时候是控制器-门面-服务-数据访问对象?

Façade通常应用于业务,可以制作您的应用程序API。数据服务只是您的服务。除此之外,您还可以在代码中的许多地方使用Façade。以此来制作SAL。因此,它成为了服务访问API。Façade是一种模式,可以制作API。 - Davut Gürbüz
4
Facade 和 Service 的主要区别在于,Service 实际上会执行某些操作,即类定义实际包含了执行某些逻辑的管道。在我看来,一个干净的 Facade 不应该持有任何逻辑,除了必要的委托实际工作给下一个 Service 的逻辑之外。 - kolossus
通过使用控制器 DAO,您不必将请求处理与逻辑分开。通常使用 Facade 将 DTO 对象转换为实体,并使用 DAO 持久化数据。因此,DAO 类与 DTO 对象解耦,控制器与实体解耦。同时,业务逻辑在 Facade 中执行,该 Facade 具有单一职责。因此,控制器 - Facade - DAO 是必须的,也可以使用服务。 - USer22999299
11个回答

72

一个服务是编写与外部系统的接口的一种方式,比如LDAP身份存储、支付网关或应用程序管理接口。这是一种将外部系统作为有用服务的提供者进行概念化的方式,可能具有内部行为,而不是被动地操作。

Facade是一种包装任何东西(包括服务)以便向另一个组件展示的方式。当:

  • 库或组件很复杂,您的应用程序只需要其中的一个子集。您的Facade向应用程序呈现简化的API
  • 您正在使用多个库或组件,并需要统一它们,向应用程序呈现一个合并的API
  • 您正在使用的库具有复杂的设置或依赖关系,facade将所有这些都包装在您的应用程序上下文中。

真正令人困惑的是,您可以(并经常)在一个或多个服务上创建Facade。服务是组件实际访问资源的方式,而facade是简化组件的部分(例如配置选项、连接等)。

如果您编写自己的DAO,您可能会根据需要创建自己的服务,因此编写facade是您做错了的迹象。如果DAO由第三方构建,并且比您的需求更复杂,则可以使用facade服务。
现在,服务是执行对多个DAO调用以获取复杂数据结构的一种方式(我不太确定,但这是我目前理解的)。
我会说DAO是一种独立的设计模式 - 请参见维基百科
如果我们将DAO与服务进行对比,我们有:
  • API级别:
    • DAO:对属性进行细粒度访问
    • Service:对服务进行粗粒度访问
  • 实现位置:
    • DAO:主要在客户端,但将数据(没有行为)存储在数据库中
    • Service:主要在服务器上
  • 接口如何调用
    • DAO:客户端直接绑定到同一命名空间和JVM中的对象
    • Service:客户端只是网络、跨vm或跨命名空间操作的存根

...外观可以完美地访问多个DAO,以通过提供简单的接口执行复杂操作,而服务似乎也在做类似的事情。

外观可以包装DAO层,但我并不真正看到这种方式有用。最可能需要一个API来访问对象的各个属性,遍历对象图形等,这正是DAO提供的。

事务也是如此,我理解服务是启动事务的地方...

绝对可以,因为事务是数据库提供的一项服务,并在另一个组件或系统上进行。但我同样认为它们也可以放在facade上,毕竟facade也可以调用多个DAO。在许多方面,事务管理器服务是一个更复杂的后端实现的facade,协调Web、应用程序、数据库和其他事务感知组件上的事务。然而,这已经被事务服务实现抽象化了。就我们用户而言,只有公共接口存在。这实际上是这些设计模式的概念点——为用户提供恰到好处的API,将实现的复杂性抽象化到组件接口的铁墙后面。那么哪种堆栈更有意义呢?控制器-外观-数据访问对象(DAO)控制器-服务-DAO,还是控制器-外观-DAO,有时还有控制器-外观-服务-DAO?
  1. The DAO是一种服务于数据库的方式,但实际上DAO本身是一种设计模式。
  2. 如果你编写自己的DAO,你永远不需要一个facade。

因此,正确答案是:

  • 控制器 - DAO

通常我喜欢将DAO视为非常原子的元素(例如,UserDAO对象可能具有getUserById()方法),它们高度内聚,因此为了执行某些操作(例如支付用户可能涉及获取用户、提取他的银行账户等),不少于调用多个DAO,提供一个Facade是否更好呢? - Juan Antonio Gomez Moriano
1
你对内聚性的观察非常关键 - 你必须暴露这些细粒度的方法,使DAO对应用程序有用(你很可能在应用程序中有一个页面来查看或编辑这些属性)。然而,门面的意图是完全隐藏这些方面,所以如果你有时直接使用它,那就不太对了。可以添加一个帮助方法来封装一个操作,比如“支付”,但这只是一个帮助方法,而不是门面。 - Andrew Alcock
感谢您的提问。那么,Facade和Helper方法之间的核心区别是什么?最终,像payUser()这样的方法完全可以成为FacadeUser类的一部分,不是吗? - Juan Antonio Gomez Moriano
归根结底,一切都是代码 - 设计模式只是谈论如何组织代码的方式,并以其目的命名。当与其他人交流时,这种方式大有帮助。这是行话。外观是一个助手方法集合,其目的是隐藏库或一组库不被应用程序访问(建筑物的外观隐藏其后面的丑陋部分)。因此,在某个级别上,您是正确的,但如果您访问其余部分,则会破坏名称的目的,从而使其他开发人员感到困惑,因此,行话不再有帮助。 - Andrew Alcock

36
Literally,Facade的意思就像名字所示,指建筑物的正面。路过的行人只能看到外立面,他们不知道里面有什么,电线、管道和其他复杂性。这个表面掩盖了建筑物的所有复杂性,并显示出一个更简单友好的面孔。
在软件术语中,Facade通过提供一个更简单的接口来隐藏其后面的软件组件的复杂性,它本身没有功能并且不限制对系统的访问。通常用于面向对象设计。一个很好的例子是SLF4J——它是一个api,是一个简单的日志系统外观,允许最终用户在部署时插入所需的日志系统。
A service是一个公共接口,提供对功能单元的访问,并总是按照规范编写。它需要支持不同消费者所需的通信契约(基于消息的通信、格式、协议、安全、异常等)。有过程服务 - 封装业务工作流、业务逻辑服务 - 封装规则/函数、数据服务 - 与实体交互、数据访问管理、基础设施服务 - 实用程序函数,如监视、日志记录和安全性。服务大多是可重用、不相关、松散耦合的功能单元。它们非常相似,但取决于您的观察角度。

我看到的区别是,Facade是从内部设计的。您查看子系统并设计Facade以提供更简单的访问。服务是从外部设计的。您查看客户/客户端定义合同并设计服务。


2
谢谢用户395072,我找不到更好的词来描述这些差异。 "Facades ..提供了一个更简单的接口,没有自己的功能,并且不限制对子系统的访问,...,服务大多是可重用的、不相关的、松散耦合的功能单元。" 这一点已经说得很清楚了。 :) - devBinnooh
1
优秀,简单明了的解释。 - i.brod

6
我对GoF Facade模式的理解是它主要用于隐藏糟糕的设计。作为一个经验法则,我认为只有遗留代码才需要Facade。
我也认为这个模式成为J2EE核心模式(Session Facade)的原因主要是因为EJB规范(至少到2.x)本身导致了糟糕的服务层设计。
因此,我的答案是:是的,Facade实际上是第一次没有正确实现的服务。如果你需要隐藏客户端代码的复杂性,通常意味着你只能提供一个库,而不是一个服务层;所以,在这种情况下,Facade实际上成为了你的服务层。
另一方面(假设你有一个体面的领域层),如果你真的需要提供使用单个方法调用生成复杂流程的选项(类似于宏/别名),这通常最好放在应用层而不是核心领域中--请注意,我已经将分层术语切换到领域驱动设计,其中没有“数据访问”或“服务”层,而是“应用程序”,“领域”,“基础设施”。

你觉得使用门面模式来管理同一API的版本控制有意义吗? - Juan Antonio Gomez Moriano

3
首先要注意的是,设计模式是对常见(设计)问题的描述,其具有标准解决方案。在某些情况下,有多种方法可以解决问题,以适合所有要求(例如,迭代器和单例模式有大量不同的实现;例如,检查Alexandrescu的作品并将其与GoF标准解决方案进行比较),而在某些情况下,不同的模式具有相同的(代码)解决方案(例如,在GoF书中比较组合和装饰者模式的类图)。
根据GoF的说法,外观模式的目的是提供一个子系统接口集的统一接口。外观定义了一个更高级别的接口,使子系统更易于使用。
服务的意图是为用户提供具有给定功能的单个高级别接口。这并不一定意味着它是外观,因为从严格意义上讲,服务并非定义为子系统中一组接口的统一接口。
但我们可以做得更好
您的问题是模式是否“相似”。如果我们认为当模式A等于B且模式B等于A时它们是“相似”的话,则应回答两个问题:
问题1:服务是否是外观?服务肯定应该公开功能,并且绝对是公开此功能的单个接口。功能通常被分解为微小的部分,因此是的,服务符合外观的基本要求。换句话说:面对将底层接口公开为统一的“服务”接口的问题,外观模式符合要求,并用于解决服务问题。答案是是。
问题2:外观是否是服务?服务通常被设计为可重用、不相关、松耦合的功能单元。考虑组件之间的通信对于服务非常重要,因为它们通常依赖于TCP/IP接口,例如SOAP或WCF。这也意味着功能经常被重新编写以更紧密地适合服务范例,这为模式添加了一个隐含的性能驱动要求。而外观没有这个额外的要求。换句话说:外观不是服务。
准确地说,这些概念是密切相关但不相同的。
但我们可以做得更好
这种思路引出了一个问题,即服务是否是外观的扩展版本?如果服务满足外观的所有要求并在此基础上进行扩展,则是。

如果你仔细阅读GoF的描述,答案是肯定的,即:如果满足一个条件:该服务必须公开子系统。实际上,我认为这个条件通常成立,否则你可能会过度设计你的服务 - 尽管严格来说这不是一个硬性限制。


3

FACADE是一种设计模式,它解决了子系统中需要统一接口的问题,定义了一个更高层次的接口,使子系统更易于使用。

然而,服务提供对资源或一组接口/对象的访问,不一定简化这样的访问。因此,您可以采用Facade模式来更好地设计您的服务,以便您可以节省客户端构建和使用它的时间。


2
通常这些术语只在特定的上下文中使用。
  • 'Facade' 的常见使用情境: 为应用程序复杂部分(如第三方库)提供简单的API。

  • 'Services' 的上下文: 解锁和展示系统中的业务实体。(SOA、DAO、安全等)

您可以将模式视为一种不断发展的语言。它似乎从来没有完美的结束,每个模式都有其自己的历史和上下文。有时候,类可以同时被视为服务和门面,有时候则不是。
例如:将第三方API称为“服务”可能被认为是误用,因为上下文错误。

有时候类可以被视为服务和外观的结合体。一个服务可以在外观中。你解释得非常清楚。 - Davut Gürbüz

2
在我试图回答之前,让我澄清一些事情:企业应用程序中有三个不同的东西 - FacadeService LayerRemote FacadeFacade - 尽管包装了子系统,但仍然是一个对象,并且UI(MVC)应用程序通常位于同一进程中。因此,通信以通常的OO方式进行:调用方法、读取属性、监听事件。 Service Layer - 当业务逻辑层变得成熟并且对MVC直接交互过于复杂时,就会在它们之间放置Service Layer。Service Layer是MVC用作业务逻辑的包装器的API。它不是远程的,也不需要使用DTO,因为通信中没有涉及到任何线路。 Remote Facade - (简单地说,任何远程服务)这是Facade和Service Layer的混合体。当您想要公开某种系统的包装器(我们称之为Facade)作为分布边界时,Remote Facade开始存在。其中一个原因可能是允许几个UI(MVC)应用程序使用相同的Remote Facade。
比较: Facade vs. Service Layer: 它们很相似,因为它们都包装子系统。区别在于Service Layer更加面向UI(MVC)应用程序的需求,并公开函数以简化与业务逻辑的交互。另一方面,Facade公开功能以简化业务逻辑,但不一定简化与UI(MVC)应用程序的通信。 Facade vs. Remote Facade (Service?): 明显是不同的,因为Remote Facade必须使用DTO作为通信消息。如果您仍然希望将其用作常规对象(属性、事件),则Remote Facade将需要某种代理;但代理将无论如何将DTO用于实际对象,即Remote Facade。
可能的流程: controller-facade-dao - 可疑,但仍然可能。Facade通常不仅用于包装DAL。除子系统外,还应该有更成熟的东西。但是,如果Facade是业务逻辑的一部分,那么是可以的。但是子系统必须更加强调。对我来说,仅包装DAL还不足以称其为Facade。 controller-service-dao - 绝对可能。许多远程服务直接通过DAL与数据库交互。 controller-facade-service-dao - 如果您将服务视为子系统,则可能会出现这种情况。
我想再添加一个有意义的内容: controller-service [layer]-facade(业务的一部分)-子系统(例如会计、自主业务)-dao - 我相信你可以翻译这个。
记住,Service(或远程Facade)可以存在于流程中的任何位置。这只是由您的分布需求所决定的。

1
一个服务接口通常代表业务关注点:执行某些操作和/或获取某些信息。服务提供者将其服务实现为内部后端服务的外观是合理的-您永远不会看到这一点。您的外观可能包装一些通用接口,其中可能包括服务接口。例如,您可能为银行帐户创建服务接口(操作:银行转账),并为本地会计记录创建本地API(我转账)。您可以引入一个外观,使用银行的服务接口并管理您的本地支票簿的“转移资金”操作。

1
“上下文”很重要。Facade和Service并不冲突。
首先,我从未听说过在MVC的上下文中使用“Service”和“Facade”。
当人们谈论Service时,更多的是指系统或组件向外界提供业务相关操作。有时您会看到“Service”与“工作单元”(因此,事务)相关。
Service还用于应用程序的某些分层方法的上下文中:我们在DAO之上有Service,Service将通过DAO访问数据,并将业务逻辑放在Service层中,类似于这样。
Facade通常用于设计模式的上下文中,重点是“隐藏复杂操作并将其公开为简单操作”。
Facade可能是也可能不是Service(Facade中的操作可能不表示工作单元,但仍然是有效的Facade),同样,Service可能是也可能不是Facade(Service可能不隐藏任何复杂操作,但它仍然是Service)。
再次强调,“上下文”才是最重要的。
例如,当您谈论应用程序的层次结构时,简单地说“XXX是访问DAO的门面”是不合理的。同样,如果您在谈论“设计方法”,则称其为“XXX是多个后端的门面”而不是在这里称其为“服务”更为合理(尽管XXX实际上是一个服务)。

1

是的,外观模式和服务层并非完全无关。有时我们将服务层实现为外观模式,以便客户端不会被过多的服务细节所困扰。服务调用/接口越简单,客户端代码就越简单易懂。

马丁·福勒(Martin Fowler)说...

服务层定义了应用程序的边界(Cockburn PloP),以及从客户端层面接口的可用操作集合。它封装了应用程序的业务逻辑,控制事务并在实现其操作时协调响应。

因此,有时将服务层用作外观模式。

Ref


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接