Java 8的内部DSL

DSL定义:Domain-specific language (名词,简称DSL): 是一种专注特定领域的有限表达法。

Martin Fowler 认为DSL区别于通常语义上的领域模型,通过一些内部的DSL表达式可以导入或构建领域模型。(DSL类似领域模型的Builder)。

这种内部DSL表达式有下面几种:
1. Method Chaining 方法链 Fluent Style流畅风格
2. Functional Sequence
3. Nested Functions 嵌套函数
4.Lambda Expressions/Closures

在Java 8中创建Martin Fowler的内部DSL Creating Internal DSLs in Java, Java 8- Adopting Martin Fowler’s approach | Java, JVM and beyond
一文以Java 8为例说明了这种内部表达式的使用。

下面以三个语义模型为例:
Graph


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;
}

@Override
public 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;
}
}

Vertex.java

ublic class Vertex implements Comparable<Vertex> {
private String label;

public Vertex(String label) {
this.label = label.toUpperCase();
}

@Override
public int compareTo(Vertex o) {
return (this.getLabel().compareTo(o.getLabel()));
}

public String getLabel() {
return label;
}

public void setLabel(String label) {
this.label = label;
}
}

方法链是由DDD创始者Eric Evans和Martifowler共同提倡的一种流畅风格的简化代码写法,如下:


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

那么如何实现这种领域模型创建的流畅简化写法呢?

首先,建立两个builder模式构建器,分别是模型Graph和edge的GraphBuilder和EdgeBuilder:


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);
}
}

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;
}

}

好了,方法链风格的代码都准备好,我们就可以使用流畅简化的风格来创建赋值模型了,客户端代码:

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 Pattern

如果说方法链风格内部会包含状态(属于一种有态用法),那么嵌套函数写法不再内部包含模型状态,这种表达法DSL是被分析被解释执行。

三个模型graph edge和Vertex的builder类如下:


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;
}

}

/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;
}

}

//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);
}
}

嵌套函数风格的写法如下:


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

lambda写法如下:


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);
});

}
)

为了实现这个目标,创建Graph和Edge两个构造器:


//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;
}

//注意这个方法参数,这是java 8支持的函数参数
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());
}
}

//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);
}

}

调用代码:


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

lambda表达式在.net中支持很好,而且还是延迟执行的;方法级联,序列 使得方法给人看起来很简洁、连贯

2013-06-03 08:47 "@banq
"的内容
方法链是由DDD创始者Eric Evans和Martifowler共同提倡的一种流畅风格的简化代码写法,如下: ...

想到个情况,对于void 方法构建个方法级联比较容易,如果下一个函数依赖上一个函数执行的结果,这个如何构建?