通过TableCellBuilder工厂,我们可以创建对每个参数都有一个调用方法的TableCell。工厂中的每个方法都表达了其参数的目的:
| selectCell(new TableCellBuilder(6).column(8)); |
最后一步是引进静态工厂方法来替代TableCellBuilder构造函数的使用,该构造函数没有表达出6代表的是什么。如我们在之前所实现的那样,我们需要将构造函数设置为private来迫使用户使用工厂方法:
| public final class TableCell { public static class TableCellBuilder { public static TableCellBuilder row(int row) { return new TableCellBuilder(row); } private final int row; private TableCellBuilder(int row) { this.row = row; } private TableCell column(int column) { return new TableCell(row, column); } } public final int row; public final int column; private TableCell(int row, int column) { this.row = row; this.column = column; } } |
现在我们只需要selectCell的调用代码中增加内容,包含对TableCellBuilder中row方法的静态import。为了刷新一下我们的记忆,这是如何实现调用selectCell的代码:
| dialog.table("results").selectCell(row(6).column(8)); |
我们的例子说明,一点点额外的工作可以帮助我们克服主机编程语言中的一些限制。正如之前提到的,这只是我们通过使用静态工厂方法和imports来改善代码可读性的很多方法中的一个。下列代码段是以另一种不同的方法利用静态工厂方法和imports来解决相同的table坐标问题:
| /** * @author Mark Alexandre */ public final class TableCellIndex { public static final class RowIndex { final int row; RowIndex(int row) { this.row = row; } } public static final class ColumnIndex { final int column; ColumnIndex(int column) { this.column = column; } } public final int row; public final int column; private TableCellIndex(RowIndex rowIndex, ColumnIndex columnIndex) { this.row = rowIndex.row; this.column = columnIndex.column; } public static TableCellIndex cellAt(RowIndex row, ColumnIndex column) { return new TableCellIndex(row, column); } public static TableCellIndex cellAt(ColumnIndex column, RowIndex row) { return new TableCellIndex(row, column); } public static RowIndex row(int index) { return new RowIndex(index); } public static ColumnIndex column(int index) { return new ColumnIndex(index); } } |
这个方案的第二个版本比第一个版本更具灵活性,因为这个版本允许我们通过两种途径来声明行和列的坐标:
| dialog.table("results").select(cellAt(row(6), column(8)); dialog.table("results").select(cellAt(column(3), row(5)); |
组织代码
相比返回中间对象的的方式来说,返回this的方式更加容易组织连贯接口的代码。前面的案例中,我们的最后结果是使用更少的类来封装连贯接口的逻辑,并且使得我们可以在组织非DSL代码的时候使用同样的规则或约定。
采用中间对象作为返回类型来组织连贯接口的代码更具技巧性,因为我们将连贯接口的逻辑遍布在一些小的类上。由于这些类结合在一起作为整体而形成我们的连贯接口,这使得将他们作为整体对待更为合理,我们可能不想将他们和DSL外的其他一些类混淆一起,那么我们有两个选择:
◆将中间对象作为内嵌类创建;
◆将中间对象至于他们自己的顶级类中,将所有这些中间对象类放入同一个包中
分解我们的系统所采用的方式取决于我们想要实现的文法的几个因素:DSL的目的,中间对象(如果有的话)的数量和大小(以代码的行数来计),以及DSL如何来与其它的代码库及其它的DSL相协调。

