DSL的核心价值体现在两方面:一是提升软件开发或使用效率,增强代码表现力;二是作为沟通媒介,实现与非技术人员的信息交互。若设计的DSL能从根本上提升生产力,其应用便具备合理性。因此,“何时需要使用DSL”的判断标准可简化为:DSL能否为具体工作提供实质性助力。若仅为炫技或追求代码形式上的独特性,则需审慎评估其必要性。
关于“如何通过DSL提升生产力”,单纯理论阐述难以充分传递价值,需结合具体案例展开分析。以下将通过实际场景切入,逐步阐释DSL在效率提升与沟通协作中的实践逻辑。
代码1-11展示了如何基于内部DSL来对方法参数进行验证。实际上,参数验证的实现能够进一步简化。此次,我们将采用外部 DSL 来检验特定参数的取值范围,具体内容如代码1-12所示:
代码1-12
@Require("1 <= age <= 100")
void create(Integer age, String name);
请读者考虑这样一个问题:如果不使用DSL的话应该如何对参数进行验证呢?基于传统编码方式,用于验证的代码可能如代码1-13所示:
代码1-13
void create(Integer age, String name) {
if (age == null || age.compareTo(1) < 0 || age.compareTo(100) > 0) {
//……
}
}
两种实现方式的差异无需过多赘述。试想,若项目中充斥着数千行类似的重复性代码,开发者将难以享受编程乐趣,反而会被烦躁与麻木包围。为提高效率,程序员可能不得不采用复制粘贴的方式,但这类看似简单的逻辑实则极易出错。对比代码1-12与代码1-13,DSL的优势一目了然——即便仅从代码行数看,前者也更显简洁清爽。
上述案例采用了轻量级DSL,已展现出显著效能。再看一个稍复杂的业务场景:电商平台的优惠规则设置常涉及多维度条件,如商品类型、交易日期、用户类型等,支持单一条件或组合条件。例如,业务人员可能会这样去描述一个优惠规则:11月1日至11月15日之间订购任何型号的苹果手机都可享受9折优惠。这一句话中包含了很多的关键信息,如:订购日期、产品类型、产品品牌、优惠值等,其中前三项信息表示优惠条件;最后一项信息表示优惠方式。还有一些关键信息是隐式的,比如“11月1日至11月15日之间”这一描述片段,指定了订购日期要“大于等于11月1日”同时还要“小于等于11月15日”。这里的“大于等于”和“小于等于”表示的是优惠条件与优惠值之间的一种关系(也可称作运算符),不注意的放很容易被忽略掉。
在进行领域建模时,应当精准捕捉业务描述里的关键信息,并且开展一定程度的抽象工作,尤其要留意不能有任何遗漏,唯有如此,所构建的领域模型才能高效地支撑业务运作。图 1.3呈现了当前案例对应的领域模型。需要注意的是,鉴于本案例具备特殊性与代表性,笔者会在后文再次运用该案例,以此来演示语法分析器的实现过程。

让我们对图 1.3所示类图做一些简单的解释。Operator表示运算符、Condition表示优惠条件、Target表示表示优惠条件的值。以需求“订购任何型号的苹果手机”为例,能够分解成如下两个优惠条件对象:产品类型=手机、品牌=苹果。以第一个条件为例,可按如下顺序映射成领域模型:Condition(产品类型)、Operator(=)和Target(手机)。类型Rate表示优惠方式,对应于需求中的“9折优惠”。当然,实际业务中的优惠方式更加复杂、多样,比如赠送某商品、减免某一额度的金额等,这就意味着需要建立一组用于表示“优惠方式”的类族,不过出于简化的目的,我们假设只有按比例优惠这一方式。
可以看到,即便是简单示例,其对应的领域模型结构已较为复杂,而真实业务场景中的模型会更加复杂。但真正的挑战并非来自领域模型本身——由于优惠条件、条件值、运算符、优惠模式等均为动态变量,前端页面需处理大量交互逻辑,导致复杂度激增。除了多层级联动(如选择“日期”条件后动态加载时间范围控件),前后端数据格式与语义的差异还会增加调试成本。此外,用户需通过繁琐的手动配置(如逐条件选择、输入值、组合逻辑)完成规则设置,操作效率低下且体验不佳。
此时正是引入DSL的最佳场景,更进一步讲,适合采用外部DSL。代码1-14展示了笔者所给出的具体实现:
代码1-14
set rate = 0.9 where current_date >= "2024-11-01" and current_date <= "2024-11-15"
and product_type = "phone" and brand = "apple";
仅一行代码而矣,运行之后便可以建立出一条优惠规则。读者可能会觉得笔者夸大了DSL的效果,毕竟DSL编译器的设计与研发也需要不少的投入。可是账并不是这样算的,编译器方面的投入更多地是一次性的,尽管随着业务的变更我们可能也需要同步对编译器进行修改,但总体工作量是可衡量的。另一方面,用户可以在很长的一段时间内无限次的通过该DSL来配置优惠规则,所节省的精力就很难用数字去评估了。理论上来说,使用次数越多、时间越久,DSL实现的总成本就变得越低。就好像我们日常编写的单元测试用例一样,一次编写、多次复用,测试的总体成本是节省的。当然,我们也的确不能为了使用DSL而去使用它,尤其是该DSL本身比较复杂且仅被有限次地使用的时候。软件建设工作等于投资,当投入大于产出的时候就应报以慎重的态度,毕竟没有人可以绕过“成本”这一关键问题。
书归正文,让我们再回到优惠规则DSL的话题上。这样的DSL真的会提升用户体验吗?人们印象中的软件往往都是界面化的,用户只需要动动鼠标即可完成一切。这一类型的软件实际上只是众多软件风格中的一种,界面化并不绝对等于方便。以Navicat数据库管理软件为例,用户可以通过鼠标的操作来构建一个查询,也可以通过编写SQL的方式。大部分情况下,后面一种方式要更加快捷且高效。因此,进行软件设计时不仅仅要考虑具体的功能,还需要对用户群的特性进行思考。以当前案例为例,设置优惠的人一般是系统运营人员,他们不仅熟悉计算机,甚至很多人都是此中高手。虽然并没有强大的IDE提供支持,但仅通过一小段DSL便可完成一个优惠的配置也要比使用鼠标的各种“点点点”操作更受欢迎,尤其是需要进行批量操作的时候,代码的方式明显要更加简洁、方便。
在对DSL形成感性认知后,需进一步探讨其另一作用:沟通。
马丁·福勒在《领域特定语言》中指出,DSL可用于与领域专家的交流。笔者对此持部分认同态度——DSL在一定程度上可作为沟通介质,但需保持审慎态度,尤其不应强制领域专家使用。语言的推广不仅是技术问题,文化适配性亦至关重要:设计者认为优秀的DSL,未必能获得目标用户的认可。因此,DSL能否成为有效沟通语言尚无明确结论,其推广效果很大程度上取决于发明者或推广者的行业影响力。例如,若有领域“权威”为DSL背书,其普及难度将显著降低。
自然语言具有高度灵活性与复杂性,若期望DSL实现类似的沟通效能,需确保其表达能力既非过于有限,亦非过度复杂——SQL是这方面的典型成功案例,但多数DSL难以企及SQL的行业地位。使用DSL进行沟通的潜在问题在于:用户可能基于个人习惯提出功能扩展需求,导致DSL快速膨胀并最终丧失可维护性。本质上,DSL作为编程语言,更适用于人机交互场景,而强制要求业务专家采用技术思维沟通并不现实。因此,是否在业务沟通中引入DSL需结合具体场景判断,企业文化、团队协作模式及管理层支持力度,均是影响DSL普及的核心变量。

84

被折叠的 条评论
为什么被折叠?



