在许多情况下,Java DSL只是一种组装一些复杂配置,然后将构建的结构传递给将对其进行处理的内部方法的方法。 例如,SQL查询构建器,HTTP请求构建器都属于此类。
首先,简单建造者在这种情况下,模式很少方便。 它对调用顺序没有限制,并且可能会导致配置不完整或仅导致代码不可读。
解决方案是拆分建造者进入相互链接的一组接口。
为了说明这种方法,让我们想象一下,我们正在尝试编写另一个非常简单的HTTP请求构建器。 使用所描述的方法,它可能看起来像这样:
public interface RequestBuilder {
static Stage1 get(String uri) {
return new Builder(Method.GET, uri, list());
}
interface Stage1 {
default Stage2 withoutParameters(Object... parameters) {
return with();
}
Stage2 with(Object... parameters);
}
interface Stage2 {
Request build();
}
class Builder implements Stage1, Stage2 {
private final Method method;
private final String uri;
private final List<?> parameters;
private Builder(final Method method, final String uri, final List<?> parameters) {
this.method = method;
this.uri = uri;
this.parameters = parameters;
}
@Override
public Stage2 with(final Object... parameters) {
return new Builder(method, uri, list(parameters));
}
@Override
public Request build() {
return new Request(method, uri, parameters);
}
}
}
建筑规范集中在建造者类。 此实现使用不可变的实例,但这不是严格必需的。
整体建造者类永远不会直接暴露给用户代码。 相反,我们返回的界面将用户操作限制为with()和没有参数()。 这些方法依次返回下一个构建阶段接口(第二阶段),允许用户完成建筑对象。
示例代码的用法如下所示。 请注意,没有办法不明确指定参数(或缺少参数):
...
RequestBuilder.get("http://somewhere.com/api/users/{param1}")
.with(userId1)
.build();
...
RequestBuilder.get("http://somewhere.com/api/users")
.withoutParameters()
.build();
...
该示例非常简单,但是实际上可能有任意数量的中间阶段,它们不一定必须将构建过程固定为单向图(例如,创建循环以组装复杂的子对象也很容易完成)。
我们得到的是:
- 无法建立不完整的物件通过IDE引导用户完成对象构建阶段,并在每个步骤中限制选择的数量通过代码进行的所有调用均遵循相同的模式,从而简化了阅读和审阅此类代码的过程正确设计和命名的整个API可以形成使用DSL的便利。