一、领域专用语言简介
1.1、什么是 DSL
DSL(Domain Specified Language)领域专用语言。
要理解什么是领域专用语言,需要先了解其创建背景。即为什么会诞生这样一种事物,其发明的目的是为了解决什么问题。
总的来说 DSL 是为了解决系统(包括硬件系统和软件系统)构建初期,使用者和构建者的语言模型不一致导致需求收集的困难。
举一个具体的例子来说。在构建证券交易系统的过程中,在证券交易活动中存在许多专业的金融术语和过程。现在要为该交易过程创建一个软件解决方案,那么开发者/构建者就必须了解证券交易活动,其中涉及到哪些对象、它们之间的规则以及约束条件是怎么样的。那么就让领域专家(这里就是证券交易专家)来描述证券交易活动中涉及的活动。但是领域专家习惯使用他们熟练使用的行业术语来表达,解决方案的构建者无法理解。如果解决方案的模型构建者要理解交易活动,就必须让领域专家用双方都能理解的自然语言来解释。这种解释的过程中,解决方案的模型构建者就理解了领域知识。这个过程中双方使用的语言就被称为“共同语言”。
共同语言称为解决方案模型构建者用来表达解决方案中的词汇的基础。构建者将这些共同语言对应到模型中,在程序中就是模块名、在数据模型中就是实体名、在测试用例中就是对象。
在上面的描述,可以看到在需求收集的过程中,如果要成功构建模型,则需要一种领域专家和构建者(也就是通常的领域分析师/业务分析师)都能理解的“共同语言”。但是这种共同语言的创建过程没有保证,不能够保证在收集过程中得到的信息完整的描述了领域活动中所有的业务规则和活动。
如果能够让领域专家通过简单的编程方式描述领域中的所有活动和规则,那么就能在一定程度上保证描述的完整性。
DSL 就是为了解决这个问题而提出的。
常见的 DSL
- 软件构建领域 Ant
- UI 设计师 HTML
- 硬件设计师 VHDL
DSL 的特点
- 用于专门领域,不能用于其他领域
- 表现力有限
- 不描述解答域,仅描述问题域
DSL 与通用编程语言的区别
- DSL 供非程序员使用,供领域专家使用
- DSL 有更高级的抽象,不涉及类似数据结构的细节
- DSL 表现力有限,其只能描述该领域的模型,而通用编程语言能够描述任意的模型
DSL 分类
要理解DSL 分类需要先理解一个概念。“元语言抽象”。
它是指通过一种语言来构建另一种语言。比如 Java 就是构建在 C 上的语言。
其中,从什么语言构建而来,这种构建来源的语言称为“宿主语言”。
根据是否从宿主语言构建而来,DSL 分为:
- 内部 DSL(从一种宿主语言构建而来)
- 外部 DSL(从零开始构建的语言,需要实现语法分析器等)
还有一种属于外部DSL的“非文本DSL”,即尽量使用图形化表述。比如 JetBrains 的 MPS。
我们通常所用的框架实际上就是一种内部 DSL。内部DSL其实更像是种别称,它代表一类特别API及使用模式。比如 Rails 框架就是基于 Ruby 的用于 Web 开发领域的内部 DSL;例如Elasticsearch里面的Query DSL:
{ "query":{ "match_all":{} } }
1.2、为什么需要DSL?
- 提高开发效率,通过DSL来抽象构建模型,抽取公共的代码,减少重复的劳动;
- 和领域专家沟通,领域专家可以通过DSL来构建系统的功能;
- 执行环境的改变,可以弥补宿主语言的局限性;
如何构建 DSL
构建DSL 要满足三个原则:
- 能够完整描述领域
- 简单易用
- 隐藏实现细节
DSL的处理可能包含那几个步骤?
- DSL脚本;
- 解析脚本;
- 语义模型;
- 生成代码或者执行模型;
为啥groovy能比较好的构建DSL?
- 不需要class文件,可以直接执行脚本;
- 闭包等特性以及语法的简介,使用非常灵活;
- 可以和java系统无缝的整合;
- Groovy自身不是 DSL。 Groovy官方已经发布了较多基于 Groovy书写的 DSL,比如 GANT, GORM, XMLBuilder, HtmlBuilder等等;
使用groovy做DSL的示例:
ParseDsl.make { to "Nirav Assar" from "Barack Obama" body "How are things? We are doing well. Take care" idea "The economy is key" request "Please vote for me" xml }
2、那必须要有地方解析这个规则:
//groovy methodMissing如果找不到方法,就会调用这个方法 里面用到了这个特性
import groovy.xml.MarkupBuilder; /** * 解析一个自定义的DSL文本的类,类似解析规则 */ class ParseDsl { String toText String fromText String body def sections = [] def static make(closure) { ParseDsl memoDsl = new ParseDsl() closure.delegate = memoDsl closure() } def to(String toText) { this.toText = toText } def from(String fromText) { this.fromText = fromText } def body(String bodyText) { this.body = bodyText } def methodMissing(String methodName, args) { def section = new Section(title: methodName, body: args[ 0 ]) sections << section } def getXml() { doXml(this) } def getHtml() { doHtml(this) } def getText () { doText(this) } private static doXml(ParseDsl memoDsl) { def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.memo() { to(memoDsl.toText) from(memoDsl.fromText) body(memoDsl.body) for (s in memoDsl.sections) { "$s.title" (s.body) } } println writer } private static doHtml(ParseDsl memoDsl) { def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.html() { head { title( "Memo" ) } body { h1( "Memo" ) h3( "To: ${memoDsl.toText}" ) h3( "From: ${memoDsl.fromText}" ) p(memoDsl.body) for (s in memoDsl.sections) { p { b(s.title.toUpperCase()) } p(s.body) } } } println writer } private static doText(ParseDsl memoDsl) { String template = "Memo To: ${memoDsl.toText} From: ${memoDsl.fromText} ${memoDsl.body} " def sectionStrings = "" for (s in memoDsl.sections) { sectionStrings += s.title.toUpperCase() + " " + s.body + " " } template += sectionStrings println template } }
1.2、何时使用 DSL
DSL 的构建难度比较大,从零构建不合适。建议使用比较完善的构建。
背后的概念
DSL 是在需求分析阶段中为了解决需求收集过程中需求描述方(遇到问题方/问题描述者[注意:遇到问题方通常是客户或者用户,而描述方通常是出现该问题的所在行业的专家,这两个角色可能是同一个人,也可能是不同的人])与解决方案提供方(构建者)间互相理解困难而设计的专门工具。在需求收集中,首先需要理解问题方遇到的问题,然后将其映射到解决方案提供方的解决技术上。用术语描述的话,问题描述者(描述领域活动的背景和问题)称为“领域专家”,其使用“行话”来说明;解决方案提供者称为“模型构建者”,其通过对领域活动及其问题的理解,加上对技术解决方案的理解,提出解决方案模型。该模型最终映射到技术方案中。这个过程中的活动成为“领域分析/领域建模”。其目的是确定并理解领域中的重要元素、过程以及其间的关系。
简单的领域建模
我(模型构建者/业务分析师):“你来描述一下你的日常工作/生活的活动和过程”
领域专家:(他会习惯使用行话来描述)
我:(不理解)”你能用我们日常使用的语言来描述一下吗?“
领域专家:(他仍然会习惯性的使用行话来进行解释)
我:(通过不断的询问来手机领域信息(即问题域)以及问题)
我:(最终整理出领域模型[需求分析的结果],接下来用技术方案(即解决域)来对问题域中的问题设计出解决方案[最终的解决方案/产品/项目])
lambda和闭包(closure)的区别是什么
lambda就是匿名函数——即没有名字的函数。前面你已经看到过一些例子了。闭包就是封闭在其定义所处环境的任意函数。
参考:https://blog.csdn.net/u010278882/article/details/50554299
转自:https://blog.csdn.net/iamzhongyong/article/details/84533930?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-3.control