什么是DSL?英文全称Domain Specific Language,中文解释为领域专用语言。顾名思义,DSL是针对某个特定领域而开发的语言。像我们平时接触到的C/C++,Java,Python/Ruby,都属于通用语言,可以为各个领域编程,通用性有余,则针对性不够强。DSL恰恰是为了弥补通用语言的这个劣势而出现的。
DSL其实并没有那么神秘。实际上,在平时的面向对象的编程中,大家会自觉不自觉的使用DSL的一些方法和技巧。比如,如果我们定义了非常面向业务的函数,然后这些函数的集合就可以被称为一种DSL了。Smalltalk的开发者Blaine Buxton 这样评价DSL:"?I believe a DSL is a healthy bi-product of a good object-oriented design"。
然而,也正是因为DSL的构建可以像定义函数一样简单,这也让很多人觉得DSL不过就是定义一些合适的函数——如果那些函数在调用时不用带上括号的话那就更好了。诚然,DSL和定义合适的函数之间是有一些类似之处,但是这恰恰是同一个问题的两面,DSL更多是从客户的角度出发看待代码,定义函数则更多的从解决问题的方案的角度看待代码。诚然两者都有交集,但是出发点却截然不同。
我引用Obie Fernandez在QCon上举的一个例子来看看定义函数和定义DSL的差别。
场景:如果你想要一杯咖啡。。。
你可以定义好一个叫做Latte的类,然后:
如果你不是这个类的设计者,你会问,第一个参数是什么意思?第二个呢?第三个?还有,那个false谁能给点提示?
或者你也可以这样:
? 我只要告诉你,order等号后面,按照你想要的类型去点就可以了。
?
从上面的例子上,我们大体可以看出定义函数和DSL之间的风格的差别了。相比较而言,DSL的风格更面向客户,语法也更加友好。有人会问:“我为什么要用DSL?DSL都是给不懂编程的人用的。” 非也。DSL说到底是为了更加快速的描述问题,解决问题的。像Capistrano(一种网络应用的部署解决方案)用DSL来定义部署操作:
? ? ? 即便我们没有学过Capistrano的语法,从上面的文件中我们也可以大体了解这些操作做了什么。上面的文件并不是什么简化的说明文档,而是实实在在可以运行的代码。
又比如Rake(Ruby版的Makefile):
对于熟悉Ruby的人来说,这个DSL基本就是Ruby的语法了,但是可以通过一些关键字完成更为强大的功能。
按照Marting Fowler的看法,DSL可以分为两种基本类型:内部DSL和外部DSL。顾名思义,外部DSL就相当于实现一种编程语言,也许不如实现一门通用语言那么复杂,但是工作量不小;内部DSL就是基于一种通用编程语言的基础上进行关键字的定义封装来达到DSL的目的,这种DSL的扩展性可能会受到母语言的影响,对于不熟悉母语言的人来说可能不是那么好理解,不过好处就是你可以利用母语言本身通用的其他功能。那么Ruby DSL自然是一种内部DSL了。