英文原文见: https://blog.csdn.net/matlab2000/article/details/6006676
五种语言的DSL
我们为客户提供5种编程语言的客户库:Ruby、Python、PHP、Cype和Java。每个库都是用来帮助我们的客户提出请求、解析响应和从网关检索数据的。我们希望他们与Braintree进行简单、直观的整合。
维护5个客户端库意味着用5种语言编写基本相同的功能。在很多情况下,这仅仅意味着句法上的差异。然而,有些特性非常复杂,因此每个库中都应该使用稍微不同的方法。
一个例子是事务搜索。因为搜索可能有点复杂,所以我们决定在五个库中的每个库中创建一个领域特定语言(DSL)。
问题
我们希望事务搜索既容易阅读,又足够深以执行复杂的查询。具体地说,我们希望允许搜索3种不同类型的字段:
- 文本字段(Text fields) — 查询精确匹配、不匹配、字符串开头、字符串结束和子字符串
- 多个值字段(Multiple value fields) — 使用一组预定义值进行查询,并将返回匹配任何给定值的所有记录
- 范围字段(Range fields)— 使用下限、上限或两者查询(都包含)
将返回与所有标准匹配的资源集合。
实例搜索准则
下面的代码示例假设用户希望搜索满足以下条件的事务:
- 订单ID以“A2D”开头
- 客户网站以“.com”结尾
- 计费第一名等于“约翰”
- 身份被授权或解决
- 金额在10到20美元之间
RUBY
策略
在Ruby中,搜索方法生成一个搜索对象到一个块。此对象包含构建搜索条件所需的方法。然后执行块,并根据结果生成请求。Ruby还重载了==,!=、>=和<=文本和范围搜索字段上的运算符。我们认为这可以提高可读性并减少语法噪声。
例子
collection = Braintree::Transaction.search do |search|
search.order_id.starts_with "a2d"
search.customer_website.ends_with ".com"
search.billing_first_name == "John"
search.status.in(
Braintree::Transaction::Status::Authorized,
Braintree::Transaction::Status::Settled
)
search.amount.between "10.00", "20.00"
end
collection.each do |transaction|
puts transaction.id
end
优势
- 方法调用中不需要括号,这样可以创建更可读的语法
- 创建请求和执行搜索的单个步骤
- 运算符重载提高了可读性
python
战略
我们的Python解决方案使用了不同的方法,因为该语言缺少块语法和多行lambda。搜索方法需要一个表示搜索条件的对象列表。这些对象中的每一个都是使用可读的方法名内联构建的。然后,搜索方法可以迭代所提供的对象以构建搜索请求。
和Ruby一样,这个实现重载操作符==,!=、>和<用于文本和范围字段上的操作。
例子
collection = Transaction.search([
TransactionSearch.order_id.starts_with("a2d"),
TransactionSearch.customer_website.ends_with(".com"),
TransactionSearch.billing_first_name == "John",
TransactionSearch.status.in_list([
Transaction.Status.Authorized,
Transaction.Status.Settled
]),
TransactionSearch.amount.between("10.00", "20.00")
])
for transaction in collection.items:
print transaction.id
优势
- 创建请求和执行搜索的单个步骤
- 易于动态创建条件
- 运算符重载提高了可读性
弱点
- 重复TransactionSearch类名
- 文本列表作为参数
PHP
战略
PHP实现与上面描述的Python解决方案类似,但感觉不太可读。同样,search方法需要一个搜索条件对象的列表,并且这些对象是在方法调用期间以内联方式创建的。但是,类方法调用的::语法以及->operator-for-instance方法调用会使代码变得更加嘈杂。
(以下暂略)
C#
暂略
java
暂略
总结
一般来说,DSL实现有3种类型:Fluent接口、可读的内联方法参数和块。我们发现流畅的接口是静态语言最有效的方法。对于Python和PHP,可读的内联方法参数似乎是最惯用的。Ruby社区倾向于使用基于块的DSL,我们为Ruby实现选择了这种方法。
在5种不同的语言中解决相同的问题是一个非常宝贵的经验。虽然结果非常不同,但我们认为每个解决方案都是健壮的和可读的。更重要的是,用这些语言工作有助于我们从相对公正的角度了解每种语言的优缺点。
如果你是一个使用这些语言的开发人员,我们很乐意听到你的反馈。你将如何用你选择的语言处理这个问题?