使用 ES decorators 构建一致性 API

简介: ![pic] 重用和一致性是程序设计中经久不衰的两个课题。在最新的 ES Proposal 中,「decorators 语法」为此带来了一定的便利,并且,很适合应用于大型的类库中。 ---------------------------------------------- ## 装饰模式 提到 decorator 大家都不会陌生,即「装饰模式」—— 我们可以在「不侵入原有

![pic]

重用和一致性是程序设计中经久不衰的两个课题。在最新的 ES Proposal 中,「decorators 语法」为此带来了一定的便利,并且,很适合应用于大型的类库中。


装饰模式

提到 decorator 大家都不会陌生,即「装饰模式」—— 我们可以在「不侵入原有代码」的情况下,为代码增加一些「额外的功能」。

所谓「额外的功能」一般都比较独立,不和原有逻辑耦合,只是做一层包装。你也可以把它看成「包装模式」。形如:

// 旧方法
function func() {}

// 包装后的新方法
function funcWrapped() {

  // 有的没的
  doSomethingBefore(); 
  
  // 旧方法的过程本身并不变化
  func();
  
  // 这啊那的
  doSomethingAfter();
}

这样看来,有一些场景特别适用这个模式,比如:

  • 记录方法的开始执行和结束执行。
  • 为运算过程提供额外的缓存能力。
  • 标记方法为 deprecated。
  • 等等。

编写一个装饰器

如果有好多方法都想包上这种「额外的功能」,那么我们不会一个个地去改写,而是考虑抽出一个「装饰器」—— 它能够接受原方法,然后生成包装后的方法。比如,我们想记录所有方法的运行时间:

function performanceTimingDecorator(func) {

  // 返回包装后的新方法
  return function(...args) {
    const start = Date.now();
    func(...args);
    const end = Date.now();
    const t = end - start;
    
    console.log(`${func.name} performed ${t}ms.`);
  };
}

function func() {}

const funcWrapped = performanceTimingDecorator(func);

// func performed 2ms.
funcWrapped();

使用 ES decorators

如果一个系统内需要大量运用装饰器,那么上述的写法可读性还有待提高。ES decorators 解决了这个问题,这是一个新的语法(语法糖):

// 定义 decorator
function performanceTiming(...args) {

  // 返回包装后的方法
  return function(target, key, descriptor) {
    // ...
  };
}

class MyClass {
  
  // 使用这种语法修饰方法 func
  @performanceTiming
  func() {}
}

新的 decorator 语法 @xxx 的形式非常类似 Java Annotation,不过后者作为静态语言,其 Annotation 的实现机制以及使用场景和 ES decorators 都有区别,这是一个题外话。事实上,ES decorators 完全借鉴自 Python 的 decorators。

同时,聪明的你应该发现,相比手写装饰器,新的语法中其实「该写的东西一个都没少」。那这个 decorators 语法有什么意义呢?

在我看来,这种语法糖对 decorators 的「定义」和「调用」都做了收敛,带来了「形式美感」。说人话,可读性更好。

  1. 在 decorators 定义时,约束了装饰器的输入(固定的几个相关参数)和输出(返回一个 function),使所有装饰器风格得到收敛。
  2. 在 decorators 调用时,以无侵入的语法「修饰」类或方法,可维护性和可读性都提升很多。

这两个优势,让我想到 ES decorators 的一个重要使用场景,便是应用于构架一致性 API。

构架一致性 API

对于多人开发的大型类库来说,「一致性」是很重要同时也很难执行的一个课题。这里的「一致性」包括:

  1. 各模块提供一致的标准公用功能。
  2. 公用功能的实现和调用方式也保持一致。
  3. 整体 API 的风格一致。

其中 1、2 两点可以通过引入 ES decorators 机制来更好地达到。

实践演示

先封装好部分 decorators(可参见 @ali/universal-decorator 这个包),这里选取两个装饰器:

  • @deprecated - 用于修饰类的方法,如果方法被调用,则在 console 中提示此方法已经过时,以便开发者转而调用其他方法。
  • @moduleLevel - 这是 Rax 体系下模块类的一个静态成员标准字段,可取值为几个有限的枚举,此装饰器对此做了约束。

接下来具体地应用到库中。

例如 @ali/universal-tracker 中,report() 方法已经迁移到了 @ali/universal-goldlog,原方法已经废弃,则可以写作:

import {deprecated} from '@ali/universal-decorator';

class Tracker {

  @deprecated('This method is moved to universal-goldlog.', {
    url: 'http://web.npm.alibaba-inc.com/package/@ali/universal-goldlog'
  })
  report() {
    // ...
  }
}

然后在调用 report() 后则会提示:

![deprecated-result]

这样,在相关的所有库中都引入类似的装饰器,从而保证 API 表达上的一致,并且这些公共逻辑遵循一致的实现。

另外还有一个例子,可以用来对类的字段做约束。以大量基于 Rax 的页面模块为例,这些模块 class 需要声明一个静态属性 moduleLevel 是 app 级别还是 page 级别,以便于框架将其渲染到对应的容器中。但是静态成员的赋值不够清晰明朗,也不能对枚举值做约束。使用 decorators 来改写则:

import {moduleLevel} from '@ali/universal-decorator';

@moduleLevel('page')
class MyModule1 {}

@moduleLevel('other')
class MyModule2 {}

moduleLevel 这个 decorator 将为类赋上一个名为 moduleLevel 的静态成员,并且会对传入值作判断,如果入参不是 'page''app',则发出警告:

![module-level-result]

最后,由于使用了 ES decorators 语法的代码,类似于一种声明式的标记,所以更利于我们对这些代码作静态分析,比如进一步的提前校验,或是条件编译等等。这部分更多的想法和思路,有待发掘。

引用

  1. Exploring EcmaScript Decorators

题图:一棵被装饰得五光十色的圣诞树。很多涉及到 decorator 的文章动不动就拿圣诞树来举例子,俨然 Christmas tree 是 decorate 的固定宾语。?

目录
相关文章
|
3天前
|
缓存 安全 API
构建高效API:RESTful设计原则与实践
【6月更文挑战第10天】在数字化时代,API作为不同软件组件之间通信的桥梁,其设计质量直接影响到应用的性能和用户体验。本文深入探讨了RESTful API的设计原则,并通过实际案例分析如何构建高效、可扩展且安全的API。我们将从资源定义、接口一致性、错误处理等方面入手,逐步揭示如何优化API设计,以满足不断变化的技术需求和业务目标。
|
3天前
|
SQL 缓存 测试技术
RESTful API设计的最佳实践:构建高效、可维护的Web服务接口
【6月更文挑战第11天】构建高效、可维护的RESTful API涉及多个最佳实践:遵循客户端-服务器架构、无状态性等REST原则;设计时考虑URL结构(动词+宾语,使用标准HTTP方法)、使用HTTP状态码、统一响应格式及错误处理;确保数据安全(HTTPS、认证授权、输入验证);实施版本控制;并提供详细文档和测试用例。这些实践能提升Web服务接口的性能和质量。
|
10天前
|
XML JSON API
RESTful API关键部分组成和构建web应用程序步骤
RESTful API关键部分组成和构建web应用程序步骤
12 0
|
10天前
|
缓存 测试技术 API
构建高效的RESTful API:最佳实践与设计原则
在数字化时代,RESTful API已成为连接不同软件系统和服务的桥梁。本文将深入探讨如何设计和实现一个既高效又易于维护的RESTful API,涵盖从请求处理到数据交互的最佳实践,以及如何确保API的安全性和可扩展性。我们将通过具体的代码示例和架构设计,展示如何构建一个遵循REST原则的API,以及如何利用现代框架和工具来简化开发过程。
|
15天前
|
JavaScript API 数据处理
构建高效可扩展的RESTful API:后端开发实践指南
【5月更文挑战第30天】在数字化转型和移动应用日益普及的背景下,构建一个高效、可扩展且易于维护的RESTful API对于后端开发者来说至关重要。本文将深入探讨设计RESTful API的最佳实践,包括如何选择合适的技术栈、实现高效的数据处理以及确保API安全性。通过阅读本文,您将了解如何从零开始构建一个高性能的后端系统,同时保证其在未来可以轻松适应业务需求的变化。
|
15天前
|
Web App开发 JavaScript Cloud Native
构建高效可扩展的RESTful API:Node.js与Express框架实践指南构建未来:云原生架构在企业数字化转型中的关键作用
【5月更文挑战第29天】 在数字化时代的驱动下,后端服务架构的稳定性与效率成为企业竞争力的关键。本文深入探讨了如何利用Node.js结合Express框架构建一个高效且可扩展的RESTful API。我们将从设计理念、核心模块、中间件应用以及性能优化等方面进行系统性阐述。通过实例引导读者理解RESTful接口设计的最佳实践,并展示如何应对大规模并发请求的挑战,确保系统的高可用性与安全性。
|
16天前
|
负载均衡 监控 Kubernetes
构建高效微服务架构:API网关与服务发现的融合实践
【5月更文挑战第29天】 在当今的软件开发领域,微服务架构已成为一种流行的设计模式,其通过将应用程序拆分为一系列小型、自治的服务来提供灵活性和可扩展性。然而,随着服务数量的增加,确保通信效率和管理便捷性成为了关键挑战。本文聚焦于如何通过API网关和服务发现机制的有效整合,优化微服务间的交互,提高系统整体性能和可靠性。我们将探讨API网关在请求路由、负载均衡、安全性增强方面的作用,同时分析服务发现对于实现服务间动态通信的重要性,并展示两者如何协同工作以支持复杂的后端系统需求。
|
16天前
|
缓存 安全 API
构建高效RESTful API的后端实践
【5月更文挑战第29天】 随着移动和Web应用的兴起,后端服务在软件架构中扮演着愈发重要的角色。本文深入探讨了构建高效RESTful API的实践方法,包括设计原则、性能优化技巧以及安全性考虑。文中不仅阐述了理论知识,还结合实例分析,旨在为开发者提供一套实用的后端开发指南。
|
16天前
|
负载均衡 监控 API
构建高效微服务架构:API网关与服务发现的融合实践
【5月更文挑战第29天】 在微服务架构中,服务的分布式特性要求精确的服务发现机制和灵活的流量控制手段。本文将探讨如何通过合并API网关和服务发现功能来优化后端服务的通信效率,降低延迟,并提升系统的可伸缩性。我们将分析传统模式下两者独立运作的弊端,并提出一种集成方案,该方案不仅能够简化系统架构,还能增强服务的自愈能力。文章还将讨论实施过程中可能遇到的挑战及相应的解决策略。
|
17天前
|
监控 Devops API
构建高效微服务架构:API网关的作用与实践构建高效稳定的云基础设施:DevOps与容器化技术融合实践
【5月更文挑战第28天】 在当今的软件开发领域,微服务架构因其灵活性、可扩展性和容错能力而备受推崇。本文将深入探讨API网关在构建微服务系统中的关键角色,包括它如何促进系统的高可用性、安全性和性能监控。我们将剖析API网关的核心组件,并借助具体实例展示如何实现一个高效的API网关来服务于复杂的微服务环境。 【5月更文挑战第28天】 随着企业数字化转型的深入,传统的IT运维模式已难以满足快速迭代和持续交付的需求。本文聚焦于如何通过融合DevOps理念与容器化技术来构建一个高效、稳定且可扩展的云基础设施。我们将探讨持续集成/持续部署(CI/CD)流程的优化、基于微服务架构的容器化部署以及自动化监