【发布时间】:2009-10-12 23:45:49
【问题描述】:
Ruby DSL 有哪些定义性特征将其与常规 API 区分开来?
【问题讨论】:
标签: ruby design-patterns dsl
Ruby DSL 有哪些定义性特征将其与常规 API 区分开来?
【问题讨论】:
标签: ruby design-patterns dsl
当您使用 API 时,您会以命令式的方式实例化对象并调用方法。另一方面,一个好的 DSL 应该是声明性的,代表问题域中的规则和关系,而不是要执行的指令。此外,理想情况下,非程序员的人应该可以阅读和修改 DSL(API 不是这种情况)。
另外请记住内部和外部 DSL 的区别。
【讨论】:
DSL(领域特定语言)是一个被过度炒作的术语。如果您只是使用一种语言的子集(例如 Ruby),那么它与原始语言有何不同?答案是,不是。
但是,如果您对源文本进行一些预处理以引入核心语言中没有的新语法或新语义,那么您确实拥有一种新语言,它可能是特定于领域的。
【讨论】:
<< 运算符以执行与 Ruby 中普通对象通常会执行的操作不同的操作,那是“新语法”吗?或者,如果它以前从未在任何语言中出现过,它是否只是一种新语法?
Ruby 的诗歌模式和运算符重载的结合确实提供了同时具有合法 Ruby 语法和合理 DSL 的东西的可能性。
XML 的持续恶化确实表明,也许内置在所有这些配置文件中的简单 DSL 并没有完全被误导......
【讨论】:
创建 DSL:
向 Object 类添加新方法,以便您可以像调用内置语言结构一样调用它们。 (见耙子)
在自定义对象或对象集上创建方法,然后让脚本文件在顶级对象的上下文中运行语句。 (见 capistrano)
API 设计:
在一个自定义对象或一组对象上创建方法,以便用户创建一个对象来使用这些方法。
将方法创建为类方法,以便用户在所有方法前面加上类名前缀。
将方法创建为用户在其自定义对象中包含或扩展以使用方法的 mixin。
所以是的,他们之间的界限很细。通过添加一个在正确上下文中运行脚本文件的方法,将一组自定义对象转换为 DSL 很简单。
【讨论】:
对我而言,DSL 和 API 之间的区别在于,如果 DSL 不是由该领域的某个人编写为 Ruby 的子语言,那么它至少可以被理解(和验证)。
例如,您可以让金融分析师在 Ruby DSL 中为股票交易应用程序编写规则,而他们永远不必知道他们正在使用 Ruby。
【讨论】:
事实上,它们是同一个东西。 DSL 通常是通过 Ruby 中的普通语言机制实现的,因此从技术上讲,它们都是 API。
但是,为了让人们将某些东西识别为 DSL,通常最终会在现有类中添加看起来像声明性语句的东西。类似于 ActiveRecord 中的验证器和关系声明。
class Foo << ActiveRecord::Base
validates_uniqueness_of :name
validates_numericality_of :number, :integer_only => true
end
看起来像 DSL,而以下不是:
class Foo <<ActiveRecord::BAse
def validate
unless unique? name
errors.add(:name, "must be unique")
end
unless number.to_s.match?(/^[-]?\d$/)
errors.add(:number, "must be an integer")
end
end
end
它们都将由普通的 Ruby 代码实现。只是一个看起来你有很酷的新语言结构,而另一个看起来相当平淡(而且过于冗长等等)
【讨论】: