使用Java实现内部领域特定语言(3)
- 摘要:领域特定语言(DSL)通常被定义为一种特别针对某类特殊问题的计算机语言,它不打算解决其领域外的问题。本文将描述怎样使用Java语言来编写领域特定语言,并将建议一些组建DSL语言时可采用的模式。
- 标签:Java DSL
尽管创建DreamCar非常简单,并且代码也十分可读,但我们仍能够使用car builder来创造更简明的代码:
// package declaration and imports public class DreamCarBuilder { public static DreamCarBuilder car() { return new DreamCarBuilder(); } private final DreamCar car; private DreamCarBuilder() { car = new DreamCar(); } public DreamCar build() { return car; } public DreamCarBuilder brand(String brand) { car.setBrand(brand); return this; } public DreamCarBuilder fuelEfficient() { car.setFuelEfficient(true); return this; } // similar methods to set field values } |
通过builder,我们还能这样重新编写DreamCar的创建过程:
DreamCar car = car().brand("Tesla") .color(RED) .fuelEfficient() .build(); |
使用连贯接口,再一次减少了代码噪音,所带来的结果是更易读的代码。需要指出的很重要的一点是,在返回this的时候,链中任何方法都可以在任何时候被调用,并且可以被调用任何次数。在我们的例子中,color这个方法我们可想调用多少次就调用多少次,并且每次调用都会覆盖上一次调用所设置的值,这在应用程序的上下文中可能是合理的。
另一个重要的发现是,没有编译器检查来强制必需的属性值。一个可能的解决方案是,如果任何对象创建和/或配置规则没有得到满足的话(比如,一个必需属性被遗忘),在运行时抛出异常。通过从链中方法返回中间对象有可能达到规则校验的目的。
1.2、返回中间对象
从连贯接口的方法中返回中间对象和返回this的方式相比,有这样一些优点:
◆我们可以使用编译器来强制业务规则(比如:必需属性)。
◆我们可以通过限制链中下一个元素的可用选项,通过一个特殊途径引导我们的连贯接口用户。
◆在用户可以(或必须)调用哪些方法、调用顺序、用户可以调用多少次等方面,给了API创建者更大的控制力 。
下面的例子表示的是通过带参数的构建函数来创建一个vacation对象的实例:
Vacation vacation = new Vacation("10/09/2007", "10/17/2007", "Paris", "Hilton", "United", "UA-6886"); |
这个方法的好处在于它可以迫使我们的用户申明所有必需的参数。不幸的是,这儿有太多的参数,而且没有表达出他们的目的。“Paris”和“Hilton”所指的分别是目的地的城市和酒店?还是我们同事的名字?:)
第二个方法是将setter方法对每个参数进行建档:
Vacation vacation = new Vacation(); vacation.setStart("10/09/2007"); vacation.setEnd("10/17/2007"); vacation.setCity("Paris"); vacation.setHotel("Hilton"); vacation.setAirline("United"); vacation.setFlight("UA-6886");
|
现在我们的代码更易读,但仍然很冗长。第三个方案则是创建一个连贯接口来构建vacation对象的实例,如同在前一章节提供的例子一样:
Vacation vacation = vacation().starting("10/09/2007") .ending("10/17/2007") .city("Paris") .hotel("Hilton") .airline("United") .flight("UA-6886");
|
这个版本的简明和可读性又进了一步,但我们丢失了在第一个版本(使用构建函数的那个版本)中所拥有的关于遗忘属性的校验。换句话说,我们并没有使用编译器来校验可能存在的错误。这时,对这个方法我们所能做的最好的改进是,如果某个必需属性没有设置的话,在运行时抛出异常。