为什么REST Api不遵循Facade设计模式


9

在将REST [api]结构与OO模型进行比较时,我看到了这些相似之处:

都:

  • 面向数据

    • REST =资源
    • OO =对象
  • 围绕数据的环绕操作

    • REST =在资源周围环绕VERBS(获取,发布,...)
    • OO =通过封装促进对象周围的操作

但是,例如,在尝试应用外观模式时,良好的OO实践并不总是站在REST api上:在REST中,您没有1个控制器来处理所有请求,并且您没有隐藏内部对象的复杂性。

2个概念之间的简单对象关系

OO和REST之间的类比

相反,REST促进资源与资源之间的所有关系的发布以及至少以两种形式的其他关系:

  1. 通过资源层次关系(ID为43的联系人由地址453组成): /api/contacts/43/addresses/453

  2. 通过REST json响应中的链接:

>> GET /api/contacts/43
<< HTTP Response {
   id: 43, ...
   addresses: [{
      id: 453, ...
   }],
   links: [{
      favoriteAddress: {
          id: 453
      }
   }]
}

对象A隐藏的基本复杂度

回到OO,外观设计模式考虑了objectA及其“ objectB客户Low Coupling之间的a,以及此objectA及其内部对象组成(objectCobjectD)。随着对象A接口,这允许开发者在极限冲击对象B对象A的内部变化(objectCobjectD),只要对象A API(操作)仍尊重。High Cohesion

在REST中,数据(资源),关系(链接)和行为(动词)分解为不同的元素,并可供Web使用。

在使用REST的过程中,我始终会对客户端和服务器之间的代码更改产生影响:因为我High CouplingBackbone.js请求Low Cohesion之间和资源之间进行更改。

我从来没有想过如何让我Backbone.js javascript application处理由REST链接促进的“ REST资源和功能发现。我知道WWW是由多台服务器提供服务的,并且必须分解OO元素才能由其中的许多主机提供服务,但是对于简单的情况,例如“保存”一个显示其地址的联系人的页面,我最终得到:

GET /api/contacts/43?embed=(addresses)
[save button pressed]
PUT /api/contacts/43
PUT /api/contacts/43/addresses/453

这导致我在浏览器应用程序上移动了保存操作原子事务处理责任(因为可以分别处理两个资源)。

考虑到这一点,如果我不能简化开发(不使用Facade设计模式),并且给客户端带来更多的复杂性(处理事务性原子保存),那么使用RESTful的好处在哪里?


1
让我明白 您说您必须使用两个REST调用来更新具有“嵌入式”(组合)链接地址的联系人,其中一个用于Contact,另一个用于其Address。您有一个Facade来处理更新的联系人。什么是使问题PUT /api/contacts/43级联更新内的对象?我有很多这样设计的API(主URL读取/创建/更新“整个”,子URL更新各个部分)。只需确保不需要更改就不会更新地址(出于性能原因)。
Anthony

@AnthonyAccioly,您正确理解。我试图通过添加一些图像来澄清我的问题。您的建议很好,这也是我得出的结论:手动控制我的请求并使用嵌入式对象仅发送一个请求以保持原子更新。尽管如此:为什么REST中的所有内容都会通过展平模型(暗示许多控制器)而使我远离OO质量或实施(封装等)。使用您的解决方案可以进行1次原子更新。不使用您的解决方案会给开发人员带来新的责任,并且在api上没有任何规则可以阻止他这样做。
阿兰(Alain)

请注意:您提到的“资源层次关系”是关于如何决定将关系信息编码为标识符(在这种情况下为URL)的问题。我不确定这种信息公开是REST的一部分还是它促进的,仅仅是人们在提出系统URL时自己做出的决定。如果您不同意,那么您有Roy Fielding的任何参考资料作为REST的一部分讨论此问题吗?
Thiago Silva

Answers:


7

我认为对象只是围绕一致的行为正确构建的,而不是围绕数据正确构建的。我会挑衅地说,在面向对象的世界中,数据几乎是无关紧要的。实际上,有可能并有时有一些永不返回数据的对象(例如“日志接收器”)或永不返回它们所传递的数据的对象(例如,如果它们计算统计属性)。

让我们不要混淆PODS(仅是结构)和具有行为的真实对象(例如Contacts您的示例中的类)1

PODS基本上是用于与存储库和业务对象进行通信的一种便利。他们允许代码是类型安全的。不多不少。另一方面,业务对象提供了具体的行为,例如验证数据,存储数据或使用其执行计算。

因此,行为是我们用来度量“内聚力” 2的方式,很容易看到在您的对象示例中存在内聚力,即使您仅显示操作顶级联系人的方法而没有显示操作地址的方法。

关于REST,您可以将REST服务视为数据存储库。面向对象设计的最大区别是(几乎)只有一种设计选择:您有四种基本方法(HEAD例如,如果计数,则更多),当然,URI有很多回旋余地,因此您可以做得很漂亮诸如传递许多 ID并返回较大结构之类的东西。不要将它们传递的数据与它们执行的操作混淆。内聚和耦合与代码有关,与数据无关。

显然,REST服务具有很高的凝聚力(每种与资源交互的方式都在同一位置)和低耦合(每种资源存储库都不需要了解其他资源)。

但是,基本事实仍然存在,REST本质上是数据的单一存储库模式。这有其后果,因为它是一种基于在慢速介质上的易于访问性而建立的范例,在这种介质上,“个性化”付出了高昂的代价:客户端通常希望执行尽可能少的操作,但同时只能接收所需的数据。这确定了要发送回的数据树的深度。

在(正确的)面向对象设计中,任何不平凡的应用程序都将执行更复杂的操作,例如通过合成。您可能有一些方法可以对数据进行更专门的操作-必须如此,因为REST是API协议,而OOD用于构建整个面向用户的应用程序!这就是为什么测量内聚力和耦合在OOD中很重要,而在REST中却微不足道的原因。

现在应该很明显,用面向对象的概念来分析数据设计并不是衡量它的可靠方法:这就像比较苹果和橘子一样!

实际上,事实证明,使用RESTful的好处(大部分)是上面概述的好处:对于慢速介质上的简单API来说,这是一个很好的模式。它是非常可缓存且可分片的。它对聊天等具有细粒度的控制。

我希望这能回答您(多方面的)问题:-)


1这个问题是称为对象关系阻抗不匹配的一大堆问题的一部分。ORM的支持者通常都在探索数据分析和行为分析之间的相似之处,但是最近ORM受到批评,因为它们似乎并未真正解决阻抗失配问题,被认为是泄漏抽象

2 http://en.wikipedia.org/wiki/Cohesion_(computer_science)


没错,我很难在很多方面展开我的问题,要明确一个问题,因为问题是基于这些方面的积累得出的“错误”结论。我现在将尝试通过许多评论回答您的观点。
阿兰(Alain)

[文本1]我使用“数据”一词从OO和REST世界中抽象出来。您将使用哪个词来抽象OO中的属性和REST中的数据结构?
2013年

@Alain的“数据”很好,但我的意思不是要混淆PODS和业务对象。当我们谈论OOD时,我们通常会谈论第二种。首先是方便,它几乎可以很容易地想到字典,结构或元组。
Sklivvz 2013年

是的,我同意,我使用Backbone.js来保存模型时使用的是简单的json结构。这是文本反映我实际编码经验的地方。
阿兰(Alain)

[文字3]这对我来说是新的。我认为凝聚力是通过使用特定关系的时间方法来衡量的……我更喜欢您查看它的方式。
2013年

1

考虑到这一点,如果我不能简化开发(不使用Facade设计模式),并且给客户端带来更多的复杂性(处理事务性原子保存),那么使用RESTful的好处在哪里?

回答“ RESTful的好处在哪里?” 在此处进行了详尽的分析和解释:http : //www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

但是,这个问题的困惑在于,它与REST的特性以及如何应对它们无关,而是假设您为示例系统设计的URL的设计与RESTful有关。毕竟,REST声明存在称为资源的事物,并且应为需要引用的事物提供标识符,但这并不表示ER模型中的实体与您创建的URL具有1-1对应关系。 (URL都不应该编码模型中ER关系的基数)。

对于联系人和地址,即使您想将这些信息提取并保存在不同的关系数据库表中,无论它们是通过PUT还是POST方式发布,您都可以定义一个资源来共同表示这些信息为一个单元。 。


1

那是因为外墙是一个“烂摊子”;您应该看一下“ api抽象”和“ api链接”。该api是两套功能的组合:I / O和资源管理。在本地,I / O很好,但是在分布式体系结构(即代理,API门,消息队列等)中,I / O是共享的,因此数据和功能变得重复并纠缠在一起。这导致了跨领域的关注。这困扰了所有现有的api。

解决此问题的唯一方法是将API的I / O功能抽象到前置/后置处理程序(例如Spring / Grails中的handlerIntercepter或Rails中的过滤器),这样该功能可以用作monad并在实例与外部之间共享工具。用于请求/响应的数据也需要在一个对象中外部化,以便可以共享和重新加载。

http://www.slideshare.net/bobdobbes/api-abstraction-api-chaining


0

如果您了解REST服务或一般的任何API,就像暴露给客户端的其他接口,以便他们可以通过该接口对控制器进行编程一样,那么突然变得很容易。该服务不过是您的业务逻辑之上的一个附加层。

换句话说,您不必像上图中那样在多个控制器之间分配业务逻辑,更重要的是,您不必这样做。用于交换数据的数据结构不需要与您在内部使用的数据结构匹配,它们可以完全不同。

现有技术水平并被广泛接受,将任何业务逻辑放入UI代码中都是一个坏主意。但是每个UI只是一种接口(UI中的I),用于控制后面的业务逻辑。因此,将任何业务逻辑放入REST服务层或任何其他API层似乎也是一个坏主意。

从概念上讲,UI和服务API之间没有太大区别。


我同意层概念,但是“通过它编程控制器”是什么意思?
2013年

1
我想强调一个事实,控制器本身就是真正的服务。围绕整个事物的界面仅仅是实现目标的一种手段。存在任何以一种或另一种方式简化对包装功能的访问的接口。GUI为人类用户执行此操作,客户端使用服务API。两个目标受众都希望通过界面背后的东西来实现某些目标。我同意“程序”可能不是最好的措辞,但“控制控制器”听起来也很尴尬;-)
JensG 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.