锁定老帖子 主题:论面向组合子程序设计方法 之 微步毂纹生
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-01-07
ajoo 写道 哎。我说跟Rete之类的有关了么?
就age0这个需求,用jaskell还是用functor都是扯淡。你要不针对rule语义作组合,死抱着apache functor就没意义。或者说apache functor对这个问题没有任何价值。jaskell对这个问题有一定价值,但是也就起一个语法封装的作用,真正的核心还得是用java对Rule接口作组合。 算了,不说了。你还是自己动手做做吧。空对空没意思。 粗略作了一下,感觉跟着实际需求走会好一些。不过整体的设计还是跟着自己的理解走,别人也不一定看得懂。 基本思路是用一个折扣表对应到一个顾客的购物清单。每一个商品对应会生成一个折扣纪录。每一个折扣策略会对一个新建的折扣表进行运算,累加的话会合成一个新的折扣表,累加的策略按照我的理解是同一种货物取最大折扣。 取最大折扣的操作同样也类似上面的累加,不过是返回最大折扣金额的那个折扣表而已。 要做到脚本解析的话,因为Functor天生是用OO来做的,这个是弱项,不过,做一个特定的脚本解析器并不是想象中那么难。 |
|
返回顶楼 | |
发表时间:2006-01-08
哪是排它率的test case?怎么测试代码都是单条规则的?
就是这样: rule1 = ...; rule2 = ...; rule3 = ...; exclusive(rule1, rule2, rule3);; 从rule1到rule3寻找第一个适用规则。 |
|
返回顶楼 | |
发表时间:2006-01-08
ajoo 写道 哪是排它率的test case?怎么测试代码都是单条规则的?
就是这样: rule1 = ...; rule2 = ...; rule3 = ...; exclusive(rule1, rule2, rule3);; 从rule1到rule3寻找第一个适用规则。 增加两个类即可,一个是Exclusive的函数,一个是终止条件谓词。 package jfun.rule.generator; import java.util.ArrayList; import java.util.List; import org.apache.commons.functor.UnaryFunction; import org.apache.commons.functor.UnaryPredicate; /** * representing the Excluse ations,execute every action According to the order which joins until the condition * return true based on the result of previouse executed action. * @author firebody * @since 2006-1-8-12:49:33 */ public class Exclusive implements UnaryFunction { private UnaryPredicate condition; public Exclusive(UnaryPredicate predicate); { super();; this.condition = predicate; } private List actions = new ArrayList();; /** * * @param action * @return */ public Exclusive addExclusiveAction(UnaryFunction action);{ this.actions.add(action);; return this; } public Object evaluate(Object obj); { for(int i=0;i<actions.size();;i++);{ UnaryFunction action = (UnaryFunction);actions.get(i);; Object result = action.evaluate(obj);; if(condition.test(result);); return result; } return null; } } 终止条件谓词: package jfun.rule.predicate; import java.util.Iterator; import java.util.Map; import jfun.domain.Discount; import jfun.rule.Util; import org.apache.commons.functor.UnaryPredicate; /** * @author firebody * @since 2006-1-8-13:02:58 */ public class IsDiscounted implements UnaryPredicate { public IsDiscounted(); { super();; } private boolean hasDiscounts(Map discountTable);{ for(Iterator iter = discountTable.values();.iterator();;iter.hasNext();;);{ Discount discount = (Discount); iter.next();; if(discount.getDiscountPrice();>0); return true; } return false; } public boolean test(Object arg0); { Util.assertIsTypedArg(arg0,Map.class);; return hasDiscounts((Map);arg0);; } public static final IsDiscounted instance = new IsDiscounted();; } 组合规则可以这样写: //ajoo: exclusive discounts public static final UnaryFunction exclusiveDiscounts();{ UnaryFunction A = discountType_A();; UnaryFunction B= discountType_B();; UnaryFunction C = discountType_C();; //(A+B); UnaryFunction composit1 = new CompositUnaryFunction(A);.add(AddDiscounts.INSTANCE,B);; //exclusive((A+B);,C); ,if (A+B); matched,then return it's result,else run the c action Exclusive exclusive = new Exclusive(IsDiscounted.instance);; exclusive.addExclusiveAction(composit1);.addExclusiveAction(C);; return exclusive; } 原来计算折扣价格的那个计算有些bug,已经修复。 测试也加上了对这些组合的测试,主要请看DiscountTypesTest.java . |
|
返回顶楼 | |
发表时间:2006-01-08
这个东西除了直接计算discount,能不能做以下事情?
规则1,计算结果为客户账户拥有者的性别 规则2,计算结果为购买日期 规则3,如果性别为女性并且购买日期在3/8,折扣10%。否则规则不适用。此规则最好能重用规则1和2。 规则4,得到客户年龄 规则5,判断客户是否为成年,判断标准为女性16,男性18,结果为bool。 规则6,如果规则5返回true,应用规则3。否则规则不适用。 |
|
返回顶楼 | |
发表时间:2006-01-08
ajoo 写道 这个东西除了直接计算discount,能不能做以下事情?
规则1,计算结果为客户账户拥有者的性别 规则2,计算结果为购买日期 规则3,如果性别为女性并且购买日期在3/8,折扣10%。否则规则不适用。此规则最好能重用规则1和2。 规则4,得到客户年龄 规则5,判断客户是否为成年,判断标准为女性16,男性18,结果为bool。 规则6,如果规则5返回true,应用规则3。否则规则不适用。 主要请看 主要代码: jfun.rule.ajoo这个包下的。 package jfun.rule.ajoo; import jfun.rule.discountType.DiscountExp; import jfun.rule.function.SetDiscount; import jfun.rule.predicate.IsAllCommodity; import org.apache.commons.functor.BinaryPredicate; import org.apache.commons.functor.UnaryFunction; import org.apache.commons.functor.UnaryPredicate; import org.apache.commons.functor.adapter.RightBoundPredicate; import org.apache.commons.functor.core.IsEqual; import org.apache.commons.functor.core.composite.Composite; import org.apache.commons.functor.core.composite.ConditionalUnaryFunction; import org.apache.commons.functor.core.composite.UnaryAnd; /** * @author firebody * @since 2006-1-8-17:13:38 */ public class Rules { public Rules(); { super();; } public static final UnaryFunction rule3();{ return new ConditionalUnaryFunction(rule3_condition();, DiscountExp.setDiscountCalculator(new SetDiscount(0.1););.addCondToDiscountedObjects(IsAllCommodity.instance);,DiscountExp.NODISCOUNT);; } public static final UnaryPredicate rule3_condition();{ UnaryAnd conditions = new UnaryAnd();; //sex is femail UnaryPredicate cond1=constructUnaryPredicate(GetCustomerSex.instance,IsEqual.instance();,"femail");; //purchase date is 3/8 UnaryFunction getDate = Composite.function(GetDate.instance,GetPurchaseDate.instance);; UnaryFunction getMonth = Composite.function(GetMonth.instance,GetPurchaseDate.instance);; return conditions.and(cond1);. and(constructUnaryPredicate(getMonth,IsEqual.instance();,new Integer(3);););. and(constructUnaryPredicate(getDate,IsEqual.instance();,new Integer(8);););; } /** * shortcuit method to construct predicate.test(leftFunction.evaluate(obj);,rightConstant); * @param leftFunction * @param predivate * @param constant * @return */ static UnaryPredicate constructUnaryPredicate(UnaryFunction leftFunction,BinaryPredicate predicate,Object rightConstant);{ return Composite.predicate(RightBoundPredicate.bind(predicate,rightConstant);,leftFunction);; } } |
|
返回顶楼 | |
发表时间:2006-01-09
如果客户性别还有“不知道”一项,而对规则1,如果客户性别不知道,则不适用。
同样的,客户年龄也有“不知道”这个选项。如果不知道,任何关于年龄的规则也不适用。 规则三里,调用了规则1,如果规则1不适用,则规则3也不适用。(实际上,推而广之,任何作为某个其它规则X的条件的规则如果不适用,则该规则X也不适用) 能不能做这么个组合子,ifthen(rule1, rule2),它产生一个新的rule,以rule1为condition,rule2为结果。rule1不适用,或者rule1返回false,都认为整个规则不适用。 |
|
返回顶楼 | |
发表时间:2006-01-09
ajoo 写道 如果客户性别还有“不知道”一项,而对规则1,如果客户性别不知道,则不适用。
同样的,客户年龄也有“不知道”这个选项。如果不知道,任何关于年龄的规则也不适用。 规则三里,调用了规则1,如果规则1不适用,则规则3也不适用。(实际上,推而广之,任何作为某个其它规则X的条件的规则如果不适用,则该规则X也不适用) 能不能做这么个组合子,ifthen(rule1, rule2),它产生一个新的rule,以rule1为condition,rule2为结果。rule1不适用,或者rule1返回false,都认为整个规则不适用。 可以把每一个Rule既作为能够获得结果的Function,由可以作为条件判断的Predicate,可以类似这样: public class Rule1 implements UnaryFunction,UnaryPredivate{ public Object evaluate(Object arg);{ //.. your logic return result; } public Boolean test(Object arg);{ Object result = evaluate(arg);; //your logic here return true or false; } } 或者用一个适配器,将UnaryFunction适配成一个UnaryPredivate,Composit已经作了这个实现。 |
|
返回顶楼 | |
发表时间:2006-01-09
好。现在你的每个Rule都要实现两个接口:
Predicate和UnaryFunction。Predicate用来表示这个rule是否apply,UnaryFunction用来直接取结果。 可是,test()和evaluate()都要执行业务逻辑。如果在某处我既需要evaluate(),还要知道rule是否适用怎么办? 另外,这种实现两个接口的要求只在原子规则上才能够被满足。当用functor库里面提供的功能进行组合的时候,你还能始终保持着两个接口? 比如CompositeUnaryFunction之类的东西一旦使用,你的Predicate就被藏了起来,不能用了。 ifthen(rule1, rule2) 这里面,rule1和rule2可能是任何Rule,这些Rule甚至可能无法用evaluate()的返回值来判断是否可用。(比如,一个Rule如果适用,将读取数据库,返回客户的配偶信息,数据库可能返回null如果没有配偶的话。此时,所有返回值都被业务逻辑占据,你根本无法用一个predicate通过返回值判断规则是否适用) 它可能复杂到:ifthen(ifthen(rule1, rule2), ifthen(rule3, rule4)); 这时候,参与计算的所有rule都要同时提供是否适用的接口和具体计算业务逻辑的接口。 |
|
返回顶楼 | |
发表时间:2006-01-10
ajoo 写道 可是,test()和evaluate()都要执行业务逻辑。如果在某处我既需要evaluate(),还要知道rule是否适用怎么办? 对于这点我不是很理解,我所理解的是规则总是由Condition和Action来构成的,Action仅仅是指单纯的动作,比如返回一个折扣点数,或者设置某个对象的状态等等,而Condition就是判断执行规则的条件,我所认同的是他们两者是分开的,虽然前面有点仓促的回答你所提的问题,但我并不完全认同这样的思路。 另外,用对象来封装组合各种组合子,确实需要一些反复构思的技巧,我想他要比一个已经定义好的规范或者解析引擎来说要远远不够“好用“,但是他多了一个好处,就是我可以在具体的设计中随心所欲的设计我的组合子,结果可以是一个返回的对象,甚至一个对象状态的修改。 这些都非常灵活。 但是因为基础设施的不够完善,带来的效率很值得考虑,除非已经有一个前期的准备和充分的设计。 在具体的项目中,规则引擎的考虑不大会是他,但是小模块范围内的规则组合,我会首选它。 引用 ifthen(rule1, rule2) 这里面,rule1和rule2可能是任何Rule,这些Rule甚至可能无法用evaluate()的返回值来判断是否可用。(比如,一个Rule如果适用,将读取数据库,返回客户的配偶信息,数据库可能返回null如果没有配偶的话。此时,所有返回值都被业务逻辑占据,你根本无法用一个 predicate通过返回值判断规则是否适用) java里面的if else总归是boolean的,我不大理解你的意思。 可以说仔细一些吗? predicate不是根据返回值来动作,而是返回一个boolean . |
|
返回顶楼 | |
发表时间:2006-01-11
firebody 写道 ajoo 写道 可是,test()和evaluate()都要执行业务逻辑。如果在某处我既需要evaluate(),还要知道rule是否适用怎么办? 对于这点我不是很理解,我所理解的是规则总是由Condition和Action来构成的,Action仅仅是指单纯的动作,比如返回一个折扣点数,或者设置某个对象的状态等等,而Condition就是判断执行规则的条件,我所认同的是他们两者是分开的,虽然前面有点仓促的回答你所提的问题,但我并不完全认同这样的思路。 对,rule的condition确实是返回bool,但是condition还是可能由其它的规则组成的。 比如一个condition的定义是客户的性别为女性,那么从数据库中读取性别这个动作可能就是一个规则。 如果不认同这一点,可以换个例子,假设我有一个规则的condition是某个计算折扣的规则返回的折扣小于10个百分点: 如果rule1返回的折扣小于十个百分点,则应用rule2。 而如果rule1不适用,或者rule2不适用,自然整个rule也不适用。 这种时候,无可避免的,规则也可以被用来组合condition。其实,仔细想想,规则和condition唯一的区别就是condition返回bool,而规则返回任意对象。从这里可以看出,实际上规则是condition的超集。 把两者统一,避免了不必要的复杂性,方便了代码重用。所有对规则的组合,可以自动地应用于condition。 firebody 写道 引用 ifthen(rule1, rule2) 这里面,rule1和rule2可能是任何Rule,这些Rule甚至可能无法用evaluate()的返回值来判断是否可用。(比如,一个Rule如果适用,将读取数据库,返回客户的配偶信息,数据库可能返回null如果没有配偶的话。此时,所有返回值都被业务逻辑占据,你根本无法用一个 predicate通过返回值判断规则是否适用) java里面的if else总归是boolean的,我不大理解你的意思。 可以说仔细一些吗? predicate不是根据返回值来动作,而是返回一个boolean . 但是你给的例子,那个判断规则是否适用的predicate,就是通过判断rule的返回值是否是一个空map来判断的。你可以实现其它的predicate来采用其它的策略,但是采用了functor,你的选择只能局限在判断返回值上,没有其它方法。这其实是一个hack,而不是一个合理的可扩展的设计。(除非你抛出异常?) 更何况,用一个单独的predicate来判断规则是否可用,等于把规则的内在属性强行割裂成两部分。给定一个Functor,没有匹配的Predicate来判断适用性,根本就不是一个逻辑上完整的规则。 |
|
返回顶楼 | |