首先,将Java中的Method分成:Subroutine和Function两种,
按照“契约式设计原则”的说法,Subroutine是有副作用的(side-effect),而Function是没有副作用,
语法上,Subroutine通常没有返回值,即void方法,而Function则有返回值,
比较明确的是,模板肯定不允许调用Subroutine,否则肯定会引入大量业务逻辑,
现在待讨论的是,模板中是否允许调用Function?
如:
${object.funtion(arg1, arg2)}
衍生出的还有静态Function:
${funtion(arg1, arg2)}
在CommonTemplate(
http://www.commontemplate.org)设计之初,
是禁止调用Function的,因为Function会使模板复杂化,通常可以用其它更好的方式表达:
1.无参的Function,可以直接去掉括号,简化成属性方式,如:
String.trim
String.toUpperCase
List.size
Number.toString
等等
2.有参的Function,也可用其它更形象化的操作符处理,如:
用Map[key]代替Map.get(key)
用List[2..4]代替List.subList(2, 4),
将String看作char[]数组,用String[2..4]代替String.substring(2, 4),用String[2]代替String.charAt(2),
用"aa" ~= "AA"(约等于) 代替 "aa".equalsIgnoreCase("AA")
等等
通常处理的Domian对象,都是POJO,基本数据类型,集合类等,
如果标准包对这些都支持的比较好,那禁掉Function看起来是可行的。
然而,标准包不可能想到用户所有的需求,
所以必需留给用户容易实现的扩展点,
以最近版本加入的orderby为例,
假设其不在标准包,而是用户自己扩展实现的,
需求定义:
实现集合类的按属性排序,如:
$for{book : books}
其中,books是一个包含Book的集合,Book是一个POJO,有title, price等属性
现需要在循环之前对books按价格排序,当然可能是升序或者降序,也可能是多个属性,
有三种方案可以实现:
1.扩展静态方法:
$for{book : orderby(books, "price")}
JSP2.0的EL和Velocity就是用的类似此方案
2.扩展对象方法:
$for{book : books.orderby("price")}
3.扩展二元操作符:
$for{book : books orderby "price"}
如果禁止方法调用,当然也应禁止静态方法,
那就只能用第三种,操作符扩展,
但如果这种需求很多,会不会使操作符的数量骤增,
表达式的可读性及易用性都会降低,
看起来,第二种方案比较合理,通过在外部给集合类扩展一个方法,
但就因为这,开启Function调用,是否应该?