Ruby 一步一步 DSL

kayakjiang · 2014年01月06日 · 最后由 zzz6519003 回复于 2019年09月23日 · 5661 次阅读

原文地址: http://baya.github.io/2013/12/06/%E4%B8%80%E6%AD%A5%E4%B8%80%E6%AD%A5dsl/

原文比较长哈,给个提纲,

大概是 12 年的 10 月份,想为公司的彩票,话费充值等业务定义一套DSL(Domain-specific language),这套 DSL 主要用于添加渠道,添加产品,配置订单重试等,于是花了些时间学习了下 Ruby 的 DSL 技术,在学习和实践的过程中,我总结了下 Ruby 中常见的 DSL 风格及其常用技术实现,并且对怎样实践 DSL 形成了一些自己的感受。

第一部分,Ruby 中常见的 DSL 风格及其技术实现

我归纳了四种风格,很可能有遗漏或者描述的不够准确,欢迎大家在后面补充指正。

  • 嵌套风格

  • 链式风格

  • 类宏风格

  • 补丁风格

对于每种风格的 DSL,我给出了一些常见的技术实现,这些技术都能在我们一些常用的 gem 中找到相应的代码实现, 比如 嵌套风格 的 DSL 可以通过yield, instance_eval, 转储&block, method_missing等实现,在 rails_admin, rack, rspec, jbuilder 等 gem 中能找到相关的代码作为例证和学习资料。

第二部分, Ruby 的 DSL 实践

在干完了一堆又一堆的脏活,累活后,我感觉世界清静了许多,人也清醒了许多,我只是一个程序员,摆在我面前的现实就是我要尽力在规定的时间内干完活,完成任务,而不是让上级怀疑我在偷懒,所以与其漫无目的去开发一种类似于 rails, activerecord 这种近乎业界标准的 DSL,还不如直面眼前的问题,一步一个脚印,快速而不失优雅的去解决眼前的问题。相对于 rails, activerecord 这种 Big DSL,我提出与之对应的一个概念 Small DSL,针对 Small DSL,我给 domain 做了一个定义,

  • 是一个 class
  • 可以接收外部数据

以上面的定义为基础,我写了一个 gem dun,这个 gem 里面只包含一个 class Dun::Land, 我们可以使用Dun::Land对 domain 进行封装, 详细的用法可以看看https://github.com/baya/dun/blob/master/README.md

为什么会写这样一个 gem?主要基于下面四点的思考,

  1. 不要设计精巧的类 精巧的东西往往很脆弱。
  2. 真的要很直接 定义一个类,就表明你知道你想干的事情是什么,你想干的事情在这个类中就能解决了,而不是山路十八弯,还要牵扯到许多其他地方的代码。
  3. 拒绝过度的设计模式 与眼花缭乱的设计模式告别,回归我们最初的 程序 = 数据结构 + 算法,确定一个类,也就确定了一个算法,实现了算法也就解决了问题。
  4. 写大量的并且可能枯燥的测试 写的每一个类都要测试,什么样的类好测试呢? 小的单一职责的解耦合的带输入输出的 类好测试,为了更好的测试,你会发现你需要写许许多多的类,而不是把方法都塞到 model, controller 里面去了。

欢迎大家贴出自己写的感觉比较好的 DSL

DSL is considered harmful

#1 楼 @bhuztez 你说的这句话肯定是正确的,DSL 用滥的话害处还非常大,但是还有用处的,至少它会督促我们这些程序员去使用一些比较优雅的方法去解决问题,提醒我们使用合适的变量名,方法名,类名等,至少这些名字要与我们要解决的问题域相融合。比如 sinatra,

get '/' do
  'Hello world!'
end

它就用了 get 方法,而不是随便起名字: got, gain, obtain 等虽然这些单词都有获得的意思,这里就有一种 DSL 的思想。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号