又是一个不用写代码的Kata,原文内容很多,总结起来其实就一句话:
设计一套系统,能够处理复杂的定价规则并且能够应对规则变更。
从代码角度来说,“处理”和“变更”是两个不太相关的概念,一般来说前者更容易实现,而后者往往需要对代码进行多次重构才能做到。
如果只考虑实现,最简单直接的办法就是把所有规则硬编码到程序中,可以预见会出现很多if或者switch语句。这种做法从某种角度来说可读性还是比较高的,做好注释的话可以很清晰地看到各条规则的处理方法,但是缺点就是很难应对规则变化。一旦规则变化,就肯定会修改源码,这是不可接受的。
如果要很好地应对变更,就需要对规则进行抽象。最常用也是大家最熟悉的抽象方法就是面向对象,不过就这个练习而言面向对象并不是很合适,原因很简单,规则之间的共性太少。
面向对象的强大之处在于抽取共同部分,大大简化逻辑,但是对于本身就共性很少的内容来说面向对象是无法发挥作用的。举个例子,如果我有10条规则,每条差别很大,如何抽象?或许你会马上想到用一个rule类,但是具体的处理逻辑呢?显然还是会出现很多if和switch语句,区别就是它们现在被包裹在类中而不是直接暴露出来,根本问题仍然没有解决。
那么应该怎么办呢?我给出的答案是使用领域特定语言,也就是DSL。
DSL的本质仍然是抽象,但是它的抽象能力比面向对象更高。面向对象可以看作是对表现形式的抽象,也就是把显而易见相同的东西抽取出来组成对象。但是DSL是对事物本质进行抽象,它是用完全不同的形式来表达同样的内容,从而屏蔽掉表现形式的差别。
换句话说,用面向对象的时候层次是这样的:
要表达的内容——类
用DSL的时候层次是这样的:
要表达的内容——DSL——类
现在大家应该明白了DSL的意义,那么到底如何设计DSL呢?这个就见仁见智了。
大概是一年半之前,我写完网站之后想做自动化测试,一开始也是直接写规则,后来发现会出现很多重复代码并且很难维护,于是我就设计了一套简单的DSL(或许称之为配置更合适),每条规则都是一个字符串,由多个字段组成,字段之间用逗号分割,每一个字段都有对应的意义,然后会有一个通用函数来进行统一的处理。
说白了,DSL的目的是把变更从核心代码转移出来,可以简单的理解成把变更从程序内部转移到了输入文件中,因此我们可以在不改动程序代码的前提下改变规则。不过这并不意味着程序永远不需要改动,在实际使用的过程中可能会发现新规则无法用之前的DSL描述,那就需要修改DSL并对应改动程序,不过总体来说使用DSL可以大大降低程序本身的改动频率。
对于我这样的理论大师实践新手,扯着么一大堆已经是极限了,大家如果感兴趣的话还是去找一些专家的书来读吧,哈哈。