martin fowler_用Java和Java 8创建内部DSL,采用Martin Fowler的方法

martin fowler

目前,我正在阅读Martin Fowler撰写的有关DSL- 特定领域语言的精彩书籍。 围绕DSL的嗡嗡声,围绕轻松支持DSL创建的语言,以及DSL的使用,使我好奇地了解和学习DSL的这一概念。 到目前为止,这本书的使用经验令人印象深刻。

马丁·福勒(Martin Fowler)在他的书中提到的DSL定义:

特定领域的语言(名词):一种表达能力有限的计算机编程语言,专注于特定领域。


DSL并不是什么新鲜事物,它已经存在了很长时间。 人们使用XML作为DSL的一种形式。 使用XML作为DSL很容易,因为我们有XSD来验证DSL,有解析器来解析DSL,还有XSLT来将DSL转换成其他语言。 而且大多数语言为解析XML和填充其域模型对象提供了很好的支持。 诸如Ruby,Groovy等语言的出现增加了DSL的采用。 例如,使用Ruby编写的Web框架Rails广泛使用DSL。

Martin Fowler在他的书中将DSL分为内部,外部和语言工作台。 当我阅读了内部DSL概念时,我使用Java作为宿主语言在自己的简单DSL上玩了一些。 内部DSL驻留在宿主语言中,并受宿主语言的语法功能约束。 使用Java作为宿主语言并不能给我真正清晰的DSL,但我已尽力使其更接近可以舒适地理解DSL的形式。

我试图创建用于创建图形的DSL。 据我所知,输入和表示图形的不同方法是: 邻接表和邻接矩阵 。 我一直发现这很难使用,尤其是在Java这样的语言中,这些语言没有矩阵作为一等公民。 在这里,我试图创建一个内部DSL,以用Java填充图形。

马丁·福勒(Martin Fowler)在他的书中强调,需要保持语义模型与DSL不同,并引入一种中间表达构建器,该构建器可以从DSL填充语义模型。 通过保持这一点,我能够通过编写不同的DSL语法和表达式生成器并同时使用相同的语义模型来实现3种不同形式的DSL。

了解语义模型

在这种情况下,语义模型是Graph类,它包含Edge实例的列表,每个Edge包含从VertexVertex以及权重。 让我们看一下相同的代码:

Graph.java

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;public class Graph {private List<Edge> edges;private Set<Vertex> vertices;public Graph() {edges = new ArrayList<>();vertices = new TreeSet<>();}public void addEdge(Edge edge){getEdges().add(edge);}public void addVertice(Vertex v){getVertices().add(v);}public List<Edge> getEdges() {return edges;}public Set<Vertex> getVertices() {return vertices;}public static void printGraph(Graph g){System.out.println("Vertices...");for (Vertex v : g.getVertices()) {System.out.print(v.getLabel() + " ");}System.out.println("");System.out.println("Edges...");for (Edge e : g.getEdges()) {System.out.println(e);}}
}

Edge.java

public class Edge {private Vertex fromVertex;private Vertex toVertex;private Double weight;public Edge() {}public Edge(Vertex fromVertex, Vertex toVertex, Double weight) {this.fromVertex = fromVertex;this.toVertex = toVertex;this.weight = weight;}@Overridepublic String toString() {return fromVertex.getLabel()+" to "+toVertex.getLabel()+" with weight "+ getWeight();}public Vertex getFromVertex() {return fromVertex;}public void setFromVertex(Vertex fromVertex) {this.fromVertex = fromVertex;}public Vertex getToVertex() {return toVertex;}public void setToVertex(Vertex toVertex) {this.toVertex = toVertex;}public Double getWeight() {return weight;}public void setWeight(Double weight) {this.weight = weight;}
}

顶点

public class Vertex implements Comparable<Vertex> {private String label;public Vertex(String label) {this.label = label.toUpperCase();}@Overridepublic int compareTo(Vertex o) {return (this.getLabel().compareTo(o.getLabel()));}public String getLabel() {return label;}public void setLabel(String label) {this.label = label;}
}

现在我们已经有了语义模型,让我们构建DLS。 您应该注意,我不会更改语义模型。 语义模型不应该改变不是硬性规定,而是可以通过添加用于获取数据或修改数据的新API来发展语义模型。 但是将语义模型紧密绑定到DSL并不是一个好的方法。 将它们分开可以帮助独立测试语义模型和DSL。

Martin Fowler提出的创建内部DSL的不同方法是:

  • 方法链接
  • 功能顺序
  • 嵌套函数
  • Lambda表达式/闭包

除了功能序列,我已经在本文中说明了3。 但是我在使用Closures / Lambda表达式时使用了Functional Sequence方法。

通过方法链接实现内部DSL

我设想我的DSL是这样的:

Graph().edge().from("a").to("b").weight(12.3).edge().from("b").to("c").weight(10.5)

为了实现这种DSL的创建,我们将必须编写一个表达式生成器,该表达式生成器允许弹出语义模型并提供使创建DSL 流畅的接口 。

我创建了2个表达式构建器-一个用于构建完整的Graph,另一个用于构建单个边。 在构建Graph / Edge的过程中,这些表达式生成器保存中间的Graph / Edge对象。 通过在这些表达式生成器中创建静态方法,然后使用静态导入在DSL中使用它们,可以实现上述语法。

Graph()开始填充Graph模型,而edge()及其后的一系列方法: from()to()weight()填充Edge模型。 edge()也会填充Graph模型。

让我们看一下GraphBuilder,它是用于填充Graph模型的表达式构建器。

GraphBuilder.java

public class GraphBuilder {private Graph graph;public GraphBuilder() {graph = new Graph();}//Start the Graph DSL with this method.public static GraphBuilder Graph(){return new GraphBuilder();}//Start the edge building with this method.public EdgeBuilder edge(){EdgeBuilder builder = new EdgeBuilder(this);getGraph().addEdge(builder.edge);return builder;}public Graph getGraph() {return graph;}public void printGraph(){Graph.printGraph(graph);}
}

还有EdgeBuilder,它是用于填充Edge模型的表达式生成器。

EdgeBuilder.java

public class EdgeBuilder {Edge edge;//Keep a back reference to the Graph Builder.GraphBuilder gBuilder;public EdgeBuilder(GraphBuilder gBuilder) {this.gBuilder = gBuilder;edge = new Edge();}public EdgeBuilder from(String lbl){Vertex v = new Vertex(lbl);edge.setFromVertex(v);gBuilder.getGraph().addVertice(v);return this;}public EdgeBuilder to(String lbl){Vertex v = new Vertex(lbl);edge.setToVertex(v);gBuilder.getGraph().addVertice(v);return this;}public GraphBuilder weight(Double d){edge.setWeight(d);return gBuilder;}}

让我们尝试一下DSL:

public class GraphDslSample {public static void main(String[] args) {Graph().edge().from("a").to("b").weight(40.0).edge().from("b").to("c").weight(20.0).edge().from("d").to("e").weight(50.5).printGraph();Graph().edge().from("w").to("y").weight(23.0).edge().from("d").to("e").weight(34.5).edge().from("e").to("y").weight(50.5).printGraph();}
}

输出将是:

Vertices...
A B C D E 
Edges...
A to B with weight 40.0
B to C with weight 20.0
D to E with weight 50.5
Vertices...
D E W Y 
Edges...
W to Y with weight 23.0
D to E with weight 34.5
E to Y with weight 50.5

您是否发现此方法比“邻接表/邻接矩阵”方法更易于阅读和理解? 这种方法链接类似于我不久前写的Train Wreck模式 。

嵌套功能的内部DSL

在嵌套函数方法中,DSL的样式不同。 在这种方法中,我会将函数嵌套在函数中以填充语义模型。 就像是:

Graph(edge(from("a"), to("b"), weight(12.3),edge(from("b"), to("c"), weight(10.5)
);

这种方法的优点是它的层次结构自然不同于方法链接,在方法链中,我不得不以不同的方式设置代码格式。 而且这种方法不会在“表达式”构建器中保持任何中间状态,即,在解析/执行DSL时,表达式构建器不保存GraphEdge对象。 语义模型与此处讨论的相同。

让我们看一下此DSL的表达式构建器。

NestedGraphBuilder.java

//Populates the Graph model.
public class NestedGraphBuilder {public static Graph Graph(Edge... edges){Graph g = new Graph();for(Edge e : edges){g.addEdge(e);g.addVertice(e.getFromVertex());g.addVertice(e.getToVertex());}return g;}}

NestedEdgeBuilder.java

//Populates the Edge model.
public class NestedEdgeBuilder {public static Edge edge(Vertex from, Vertex to, Double weight){return new Edge(from, to, weight);}public static Double weight(Double value){return value;}}

NestedVertexBuilder.java

//Populates the Vertex model.
public class NestedVertexBuilder {public static Vertex from(String lbl){return new Vertex(lbl);}public static Vertex to(String lbl){return new Vertex(lbl);}
}

如果您已经观察到上面定义的表达式构建器中的所有方法都是静态的。 我们在代码中使用静态导入来创建我们开始构建的DSL。

注意:我对表达式构建器,语义模型和dsl使用了不同的软件包。 因此,请根据您使用的软件包名称更新导入。

//Update this according to the package name of your builder
import static nestedfunction.NestedEdgeBuilder.*;
import static nestedfunction.NestedGraphBuilder.*;
import static nestedfunction.NestedVertexBuilder.*;/**** @author msanaull*/
public class NestedGraphDsl {public static void main(String[] args) {Graph.printGraph(Graph(edge(from("a"), to("b"), weight(23.4)),edge(from("b"), to("c"), weight(56.7)),edge(from("d"), to("e"), weight(10.4)),edge(from("e"), to("a"), weight(45.9))));}
}

输出为:

Vertices...
A B C D E 
Edges...
A to B with weight 23.4
B to C with weight 56.7
D to E with weight 10.4
E to A with weight 45.9

现在来了有趣的部分:如何在DSL中利用即将到来的lambda表达式支持。

使用Lambda表达式的内部DSL

如果您想知道Lambda表达式在Java中的作用,请在此花一些时间,然后再继续进行操作。

在本示例中,我们还将坚持此处描述的相同语义模型。 该DSL利用功能序列以及使用lambda表达式支持。 让我们看看我们如何希望最终的DSL像这样:

Graph(g -> {g.edge( e -> {e.from("a");e.to("b");e.weight(12.3);});g.edge( e -> {e.from("b");e.to("c");e.weight(10.5);});}
)

是的,我知道上面的DSL充满了标点符号,但是我们必须忍受它。 如果您不喜欢它,那么可能会选择其他语言。

在这种方法中,我们的表达式构建器应接受lambda expression / closure / block,然后通过执行lambda expression / closure / block填充语义模型。 此实现中的表达式生成器以通过方法链接在DSL实现中相同的方式维护GraphEdge对象的中间状态。

让我们看一下表达式构建器:

GraphBuilder.java

//Populates the Graph model.
public class GraphBuilder {Graph g;public GraphBuilder() {g = new Graph();}public static Graph Graph(Consumer<GraphBuilder> gConsumer){GraphBuilder gBuilder = new GraphBuilder();gConsumer.accept(gBuilder);return gBuilder.g;}public void edge(Consumer<EdgeBuilder> eConsumer){EdgeBuilder eBuilder = new EdgeBuilder();eConsumer.accept(eBuilder);Edge e = eBuilder.edge();g.addEdge(e);g.addVertice(e.getFromVertex());g.addVertice(e.getToVertex());}
}

EdgeBuilder.java

//Populates the Edge model.
public class EdgeBuilder {private Edge e;public EdgeBuilder() {e = new Edge();}public Edge edge(){return e;}public void from(String lbl){e.setFromVertex(new Vertex(lbl));}public void to(String lbl){e.setToVertex(new Vertex(lbl));}public void weight(Double w){e.setWeight(w);}}

GraphBuilder您会看到两行突出显示的代码。 它们利用Java 8中引入的功能接口 Consumer 。

现在,让我们使用上述表达式构建器来创建我们的DSL:

//Update the package names with the ones you have given
import graph.Graph;
import static builder.GraphBuilder.*;public class LambdaDslDemo {public static void main(String[] args) {Graph g1 = Graph( g -> {g.edge( e -> {e.from("a");e.to("b");e.weight(12.4);});g.edge( e -> {e.from("c");e.to("d");e.weight(13.4);});});Graph.printGraph(g1);}
}

输出为:

Vertices...
A B C D 
Edges...
A to B with weight 12.4
C to D with weight 13.4

至此,我结束了这段繁重的代码。 让我知道是否您想把它吐到3个帖子中-每个DSL实现一个。 我将其放在一个地方,这样可以帮助我们比较3种不同的方法。

总结一下:

    • 在这篇文章中,我谈到了DSL, 即 Martin Fowler在《 领域特定语言 》一书中提到的DSL。
    • 为实现内部DSL的三种方法分别提供了一种实现:
        • 方法链接
        • 嵌套函数
        • 具有函数序列的Lambda表达式

参考: 使用Java,Java 8创建内部DSL 8 :在Experiences Unlimited博客中采用了JCG合作伙伴 Mohamed Sanaulla的Martin Fowler的方法 。

翻译自: https://www.javacodegeeks.com/2013/06/creating-internal-dsls-in-java-java-8-adopting-martin-fowlers-approach.html

martin fowler

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/348294.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2021高考厦门一中成绩查询,2021年厦门中考成绩和分数线什么时候公布(附查询入口)...

每年中考结束后很多考生和家长都很关心成绩什么时候公布&#xff0c;中考分数线什么时候公布&#xff1f;然而你离彻底解放就只差一步——查分数&#xff01;查分数&#xff0c;比上考场考试还要紧张啊&#xff01;考分不知道&#xff0c;玩耍似心跳。那么2019年厦门中考成绩什…

Java开发人员应该知道的前20个库和API

优秀且经验丰富的Java开发人员的特征之一是对API的广泛了解&#xff0c;包括JDK和第三方库。 我花了很多时间来学习API&#xff0c;尤其是在阅读了Effective Java 3rd Edition之后 &#xff0c;Joshua Bloch建议在Java 3rd Edition中使用现有的API进行开发&#xff0c;而不是为…

计算机桌面文字重影,电脑桌面字有重影怎么办

1.电脑字体重影怎么办可以尝试以下4种方法&#xff1a;1. 右击“我的电脑”&#xff0c;依次单击“属性/高级/性能设置”在“视觉效果”页中将“在桌面上为图标标签使用阴影”选中&#xff0c;单击确定即可。2. 右键桌面空白处右击&#xff0c;在“排列图标”里去掉“锁定桌面的…

打磨锤子计算机专业,钳工磨锤子实训心得体会

钳工磨锤子实训心得体会实训是职业技能实际训练的简称,是指在学校控制状态下,按照人才培养规律与目标,对学生进行职业技术应用能力训练的教学过程。钳工磨锤子实训心得体会&#xff0c;我们来看看。钳工磨锤子实训心得体会1前言&#xff1a;通过这次的钳工实习报告&#xff0c;…

吸气剂和二传手被认为有害

Java程序员习惯性地用“ getters”和“ setters”来修饰类&#xff0c;这种做法根深蒂固&#xff0c;以至于几乎没有人质疑为什么这样做或是否应该这样做。 最近&#xff0c;我认为最好不要这样做&#xff0c;并且我开始在编写的Java代码中避免使用它。 在这篇博客文章中&#…

html编辑器设置为publisher,将PDF转换为Publisher的简单方法

二、 如何把PDF文件转换为Publisher支持的图片格式一些专业的软件或者在线网站都可以帮助我们把PDF文件转换为Publisher支持的图片格式。下面将提供几种支持PDF文件转换为图片格式的方法。(1) 使用都叫兽™PDF转换器进行格式转换热点推荐 - ADs都叫兽™PDF转换器 - 多功能的PDF…

Oracle应用容器云上的WildFly Swarm

在此博客文章中&#xff0c;我将描述如何将打包在WildFly Swarmber -jar中的CloudEE Duke应用程序部署到Oracle Application Container Cloud 。 在Oracle Application Container Cloud中进行部署所需的部署工件是一个ZIP归档文件&#xff0c;其中包含应用程序ber-jar和清单文…

适合学计算机用的机械键盘,一款好用的机械键盘应该怎么选?看完这篇就明白了...

一款好用的机械键盘应该怎么选&#xff1f;看完这篇就明白了2019-07-30 15:53:134点赞14收藏3评论今天给大家带来好物推荐第1期——机械键盘。作为在办公室办公的从业者们&#xff0c;平时工作中与电脑的接触时间最多。而人与电脑的交互主要靠键盘和鼠标&#xff0c;其中使用最…

六年级计算机课学什么时候,六年级信息技术《进一步了解计算机》教学设计

六年级信息技术《进一步了解计算机》教学设计教学目标知识与技能&#xff1a;1.了解计算机的五大部件2.了解各种典型的、常见的输入设备、输出设备、存储器等。过程与方法&#xff1a;通过学生利用教师提供的主题资源网站自主学习&#xff0c;了解计算机五大部件。情感、态度与…

asciidoc文件阅读_可搜索的文件? 是的你可以。 选择AsciiDoc的另一个原因

asciidoc文件阅读Elasticsearch是一个基于Apache Lucene的灵活&#xff0c;功能强大的开源&#xff0c;分布式实时云搜索和分析引擎&#xff0c;可提供全文搜索功能。 它是面向文档且无架构的。 Asciidoctor是一个纯Ruby处理器&#xff0c;用于将AsciiDoc源文件和字符串转换为…

专转本计算机专业录取分数线,2018江苏专转本各专业分数线一览!

原标题&#xff1a;2018江苏专转本各专业分数线一览&#xff01;2018江苏专转本分数线梳理018江苏专转本考试已过去四个月&#xff0c;分数线已经公布了三个多月。按照分数线进行梳理大致情况如下&#xff1a;300分以上院校专业常州大学的财务管理 320分独占鳌头。300分以上分数…

坚实原则:开放/封闭原则

先前我们讨论了单一责任原则。 关于实体原则首字母缩写&#xff0c; 打开/关闭原则是该行中的第二个原则。 “软件实体&#xff08;类&#xff0c;模块&#xff0c;功能等&#xff09;应打开以进行扩展&#xff0c;但应关闭以进行修改” 通过采用该原理&#xff0c;目标是在不…

trim函数 html,trim、stripslashes、htmlspecialchars函数

通过 PHP 验证表单数据我们要做的第一件事是通过 PHP 的 htmlspecialchars() 函数传递所有变量。在我们使用 htmlspecialchars() 函数后&#xff0c;如果用户试图在文本字段中提交以下内容&#xff1a;location.href(http://www.hacked.com)- 代码不会执行&#xff0c;因为会被…

有效的Java第三版有哪些新功能?

自从听说即将出版的有效Java 第三版以来&#xff0c;我一直想知道其中有什么新内容。 我假设将涵盖自Java 6以来引入Java的功能&#xff0c;的确如此。 但是&#xff0c;第三版Java开发人员经典版也有一些其他更改。 在本文中&#xff0c;我提供了有关在第三版中添加&#xff0…

综合知识计算机类编制,天津事业编综合知识是什么

天津事业编综合知识有三类&#xff0c;分别是文字综合类、财会类、计算机类&#xff0c;考试时根据岗位考试要求进行一类综合知识的考核&#xff0c;均为主客观性试题&#xff0c;考试时限为150分钟&#xff0c;满分为100分。综合知识(文字综合类)简介(一)测试内容综合知识(文字…

哥大计算机科学学费,哥大计算机科学专业成功案例解析!!!

学生背景&#xff1a;学生&#xff1a;刘同学本科背景&#xff1a;北京大学 生物工程专业GPA: 3.5/4.0雅思7.5 GRE320录取院校&#xff1a;哥伦比亚大学—MS in Computer Science计算机科学硕士规划分析&#xff1a;1、背景分析L同学本科读的是生物工程专业&#xff0c;因为同学…

moxy json介绍_MOXy是GlassFish 4中新的默认JSON绑定提供程序

moxy json介绍GlassFish 4现在可以提供完整的Java EE 7&#xff08;JSR-342&#xff09;平台。 EclipseLink为该发行版做出了一些重大贡献。 首先是提供JPA 2.1&#xff08;JSR-338&#xff09;实现。 我将在本文中介绍的第二个内容是EclipseLink MOXy&#xff0c;它现在是JAX-…

大学计算机科技论文格式,《中国科技论文在线》稿件格式

《中国科技论文在线》期刊以中国科技论文在线网站http://www.paper.edu.cn/ 为基础,对在线发表论文进行评审&#xff0c;将评选的优秀论文作为期刊的主要稿源。稿件格式如下&#xff1a;中文标题(20字以内)作者11&#xff0c;作者22&#xff0c;……(1. 学校 院系&#xff0c;城…

使用Spring Boot和React进行Bootiful开发

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕&#xff1f; 尝试使用Okta API进行托管身份验证&#xff0c;授权和多因素身份验证。 在过去的几年中&#xff0c;React受到了很多积极的报导&#xff0c;使其成为Java开发…

计算机图形学结课论文,计算机图形学结课论文精要.doc

计算机图形学结课论文精要2017届结课论文《计算机图形学基础教程》—小球的弹跳运动学生姓名学 号所属学院专 业 计算机科学与技术班 级塔里木大学教务处制目录摘 要11.背景11.1计算机图形学概述11.2计算机图形画面的分类22.OpenGL概述22.1程序的基本结构32.2状态机制43.方案论…