锁定老帖子 主题:再次小结领域模型的种种观点
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-01-02
ajoo 写道 firebody 写道 ^_^,再仔细想了一下,决定利用这几天空闲的时间作出一个beta的RuleEngine来,现在有点兴奋,感觉这是一个非常的思路,前期的工作可能大一些,但是对于需要灵活定制、修改策略的需求来说,这样的一个基于Script的Rule Engine还是很有必要的,各位,可否告诉我已经有人做了这样类似的咚咚,如果没有人,^_^,我要开始干活了,把它放到SF上去。
drools之类的不是就做这个活的么? 我的rule ToolKit的目的和它的不同。 在作Rule Engine之前,我需要做一个类似Hashkell或者Lamba之类的函数解析器,虽然有点转牛角尖的感觉,不过感觉作起来挺有意思的,让组合子能够互相组合,互相运算,感觉有点排骨开天地的感觉,组合好了,就随便定义函数,然后解析运行,你可以就着你的想象定义函数,然后让函数按照数学含义来运行。 作着感觉劳动量有点大,不过已经开了头了,不想一下子就气馁,至少这个元旦我还是想用它来尝试一下的,目前已经进展到可以这样定义函数和运行得出结果: script: define function sum(x,y); as{ x+y*2+1+4/2}; define function anoSum(x); as{ x*sum(x,x+1); );; appCode: //retrieve parsed function Func func = engine.parse(scirptPath);.getFunction("anoSum");; //input the param Object result = func.addArgument(1);.getResult(engine);; assertEquals(new Double(8);,result);; 也可以实现递归函数: define function F(x); as{ if(x==0);{1} else{x*F(x-1); } } 相当于这么一个数学定义: F(x); = {x==0: 1} or { x*F(x-1);} 如果这个函数解析器完善了,我想我可以以它为基础作很多基于项目的应用了,比如典型的Rule engine 。 因为要考虑到可扩展性,我里面的设计基本按照前面我提出那几个原则来设计,比较主要的是用户可以定制或者重载操作符号,函数参数的翻译接口,已经简单方面的Function接口,可以让用户自己定义函数: 比如用户可能需要这么一个元数据类型的规则,判断当前日期是否位于某个期限内: isInDiscountTime('2005/11/11','2006/1/12') ,假设用户可以定义这么一个函数,那么我需要用户可以自己负责解析参数。 利用函数解析器,用户可以在其中架起script 和 java code之间的侨联,利用java写一些复杂的函数,拥有意义的参数,那么通过脚本传入不同的参数,就可以实现更灵活的定制了。 函数的组合也很关键,比如可以这样: define func1(x,y); as {..} define func1(x); as {...} define func3(x,y); as {...} define func(); as{ func3(func1(1,2);,func2(3); );+2+3 } |
|
返回顶楼 | |
发表时间:2006-01-03
偶的jaskell就是这样的东西呀。呵呵。
设计语言之前,你得先想好几点: 1。dynamic scope vs. static scope 2。call-by-value, call-by-need or call-by-name? 3。支持currying么? 4。强类型?动态类型? 5。语言有没有java规范内的动态语义?比如你在script里面写个1,它输出到java里是Integer? Long? BigInteger? 6。支持什么样的数据结构?list?hashtable?string?regular expression?tuple? 7。支持pattern match么? 8。支持monad的do-notation么?(这个很重要,否则在组合高阶逻辑方面用lamda函数还是比较难看。) 暂时先想到这么多。 另外,你这个不是函数组合呀。如果用call-by-value语义,你组合的是函数返回值,而不是函数。 高阶组合可以比这个复杂多了的。比如: do v1<- rule1 v2 <- rule2 return (v1>v2); |
|
返回顶楼 | |
发表时间:2006-01-03
ajoo 写道 偶的jaskell就是这样的东西呀。呵呵。
设计语言之前,你得先想好几点: 1。dynamic scope vs. static scope 2。call-by-value, call-by-need or call-by-name? 3。支持currying么? 4。强类型?动态类型? 5。语言有没有java规范内的动态语义?比如你在script里面写个1,它输出到java里是Integer? Long? BigInteger? 6。支持什么样的数据结构?list?hashtable?string?regular expression?tuple? 7。支持pattern match么? 8。支持monad的do-notation么?(这个很重要,否则在组合高阶逻辑方面用lamda函数还是比较难看。) 暂时先想到这么多。 另外,你这个不是函数组合呀。如果用call-by-value语义,你组合的是函数返回值,而不是函数。 高阶组合可以比这个复杂多了的。比如: do v1<- rule1 v2 <- rule2 return (v1>v2); 可以这样理解吗? v1 = rule1; v2 = rule2; return (v1>v2); Haskell中的<-操作符其实就是相当于定义一个局部变量,保存右边表达式返回的值。 我现在已经全部实现了我所能想到的,现在我正在看Haskell,对里面灵活而又富有意义的组合操作符很感兴趣,另外从Hashkell我可以感觉到,用计算机语言对函数的运算,目前只能做到: 1) 可递归的运算模型,如果要做一个比较复杂的操作,那么这个可递归的运算模型需要比较精巧的设计(在函数式语言中,可以通过组合来避免java/c++ 冗长的代码 ) 2) 有限维的发散计算。 比如对于一个序列1....n,只要求真对某个函数计算到特定长度的值。 至于你说的 4。强类型?动态类型? 5。语言有没有java规范内的动态语义?比如你在script里面写个1,它输出到java里是Integer? Long? BigInteger? 这里感觉还是比较复杂的,Haskell用可定义输入,输出类型。 这是几个可行的思路: 1)一种做法就是让用户自己解析,我只给他一个字符串,它负责解析函数体和参数里面的所有字符。 2)针对集中常用类型,让用户定制特定的解析器,比如数字,日期。数字用户可能需要BigDecimal . 另外,+.-.*./,如果涉及到精确的计算,用户可以自己重载这些基本操作符。 常用的数据结构: 6。支持什么样的数据结构?list?hashtable?string?regular expression?tuple? 我想这些可以定制,比如一个list,可以这样做: define list as {class:jfun.fp.listFunc}; define tail as{class:jfun.fp.tailList}; define head as {class:jfrun.fp.HeadList}; list的组合操作符: ++,:都可以通过定义操作符号来实现 用户可以这样写函数script: define func1(); as{ tail(list(1,2,3,4););++list(5,6); }; 将返回list ,(2,3,4,5,6);. |
|
返回顶楼 | |
发表时间:2006-01-03
引用 Haskell中的<-操作符其实就是相当于定义一个局部变量,保存右边表达式返回的值。
这个错的有点离谱了。"<-"和"="完全是两个不同的东西。怎能混为一谈? 右面表达式的值是一个高阶逻辑对象。而"<-"左边的符号代表的是这个高阶对象一旦运行所产生的结果。如果用"=",左边的符号才是这个表达式的值:那个高阶对象。 你对1和2的回答也好像答非所问。或者我的思维还不够发散? 我刚刚完成了用jaskell给我的这个rule combinator包装的外壳,结果程序如下: final_discount where Calendar = jaskell.java.import "java.util.Calendar"; isMonth m = eq(m, cre.datepart "MONTH");; isDay d = eq(d, cre.datepart "DAY_OF_MONTH");; purchase ctxt = ctxt.getPurchasedProducts[]; purchased product = do {products=purchase} $ products.contains[product]; purchased_all products = cre.and(jaskell.prelude.map purchased products);; discount d = return (1-d);; cardtype ctxt = ctxt.getCardType[]; gender ctxt = ctxt.getGender[]; discountByMember type v = (eq type cardtype);.then (discount v);; gold_member = discountByMember "gold" 0.1; silver_member = discountByMember "silver" 0.05; platinum_member = discountByMember "platinum" 0.2; by_member = cre.any[platinum_member, gold_member, silver_member]; is_female = eq "female" gender; is_female_day = cre.and[isMonth(Calendar.MARCH);, isDay 8]; female_discount = discount 0.05 `when cre.and[is_female, is_female_day]; tvspeaker = discount 0.05 `when purchased_all["tv", "speaker"]; tvspeakerdvd = discount 0.07 `when purchased_all["tv", "speaker", "dvd"]; by_purchase = cre.any[tvspeakerdvd, tvspeaker]; final_discount = cre.fold (*); 1 [by_member, female_discount, by_purchase]; end 那个"do{product=purchased} $"对应的就是"do product<-purchased"。 测试通过。 |
|
返回顶楼 | |
发表时间:2006-01-03
右面表达式的值是一个高阶逻辑对象。而"<-"左边的符号代表的是这个高阶对象一旦运行所产生的结果。如果用"=",左边的符号才是这个表达式的值:那个高阶对象。 那我所说的=就是指高阶对象运算产生的结果。 可能表达不大一致罢了 ,我理解的fp是没有引用赋值的概念的。 另外,看了你写的那个脚本,感觉很难看懂。 语法不是很亲切,其实Hashkell的思想很好,但是语法不好,我觉得不应该再使用那种晦涩难懂的语法。 另外,关于java的函数组合,apache已经作了很多尝试,其中有一个已经成形的库:org.apache.commons.functor. 里面主要作了闭包,函数组合的封装。 全部是对象的方式。 主要作了可以传递函数对象的形式,并提供特有的绑定支持。部分实现currying功能。 这些都可以用来组合实现复杂的业务规则。 ,以前没看过FP的东东,基础很差阿。很多东西理解错了,大家别笑话阿。 赫赫 |
|
返回顶楼 | |
发表时间:2006-01-04
firebody 写道 右面表达式的值是一个高阶逻辑对象。而"<-"左边的符号代表的是这个高阶对象一旦运行所产生的结果。如果用"=",左边的符号才是这个表达式的值:那个高阶对象。 那我所说的=就是指高阶对象运算产生的结果。 可能表达不大一致罢了 ,我理解的fp是没有引用赋值的概念的。 另外,看了你写的那个脚本,感觉很难看懂。 语法不是很亲切,其实Hashkell的思想很好,但是语法不好,我觉得不应该再使用那种晦涩难懂的语法。 另外,关于java的函数组合,apache已经作了很多尝试,其中有一个已经成形的库:org.apache.commons.functor. 里面主要作了闭包,函数组合的封装。 全部是对象的方式。 主要作了可以传递函数对象的形式,并提供特有的绑定支持。部分实现currying功能。 这些都可以用来组合实现复杂的业务规则。 ,以前没看过FP的东东,基础很差阿。很多东西理解错了,大家别笑话阿。 赫赫 其实jaskell很类似lisp,相当地贫语法。多数东西都是函数啊函数。你所谓的“看不懂”大概还是一些函数怎么用没懂。即使haskell的语法我也不认为有什么晦涩的,比java简洁多了。你不喜欢主要还是先入为主罢了。习惯了不加括号的函数调用方式就好了。要说难懂,也是高阶函数组合比较难懂。 所涉及到的语法点: [list]1。函数调用可以用f(a,b)的形势,也可以用f a b的形势。 2。反单引号"`"把函数用中缀语法调用。"a `when b"相当于 "when(a,b)"。 3。"$"用来改变优先级。"f $ a b"相当于"f(a(b))"。而没有"$"的话,"f a b"相当于"f(a,b)"。 4。"a.method"就是oo的语法,相当于对对象"a"调用"method"。它也用来从namespace中引用对象——因为namespace本身也是一个对象。 5。[a,b,c]是一个list。 6。{a=b}是一个tuple。类似于一个field a的值为b的object。[/list:u] 其它就都是函数了。"and", "or", "any", "when", "then", "eq","return"等等等等。 不过你能给出更好的语法也好啊。 上面的script和我前面给出的java测试代码是完全对应的。只不过加了一个script外壳。那个java程序是: private Rule getRule();{ final Rule gold_member = MyRules.discountByMember("gold", 0.1);; final Rule silver_member = MyRules.discountByMember("silver", 0.05);; final Rule platinum_member = MyRules.discountByMember("platinum", 0.2);; final Rule by_member = MyRules.any(new Rule[]{platinum_member, gold_member, silver_member});; final Rule is_female = MyRules.isGender("female");; final Rule is_female_day = MyRules.isMonth(Calendar.MARCH); .and(MyRules.isDay(8););; final Rule female_discount = is_female.and(is_female_day); .then(MyRules.discount(0.05););; final Rule tvspeaker = MyRules.purchased(new String[]{"tv","speaker"}); .then(MyRules.discount(0.05););; final Rule tvspeakerdvd = MyRules.purchased(new String[]{"tv","speaker","dvd"}); .then(MyRules.discount(0.07););; final Rule by_purchase = MyRules.any(new Rule[]{tvspeakerdvd, tvspeaker});; final Rule final_discount = MyRules.productDouble(new Rule[]{ by_member, female_discount, by_purchase });; return final_discount; } apache的functor?只不过实现了一些最简单的组合, 个人认为实用价值真的没什么. |
|
返回顶楼 | |
发表时间:2006-01-04
发现搞这FP的咚咚,很容易有”走火入魔”的迹象。
赫赫,用它来做一些事情还是很有意思的。 不过的先放一段时间,这个元旦 差点走火入魔了。 |
|
返回顶楼 | |
发表时间:2006-01-09
现在想想,其实CO(FP?)关注的是更细小的单位(语句级别上逻辑的封装)上的重用,OO关注的是中等粒度的单位(方法和操作数据的封装)上的重用,他们是不能互相替代的吧?
Functor(CO/FP) < Object(OO) < Component(Midware) < Service(SOA) ? 回到之前的问题, pig345 写道 CO方式,能否用来做Domain的设计/实现?
或者,怎么摆脱分类的方式建模? |
|
返回顶楼 | |
发表时间:2006-01-09
这个理解和我的比较相似。
我也认为co适合在用OO把问题分解到一定的粒度后进行。不管需求的变化多复杂,问题域需要集中,高内聚。 不过OO我也不认为分类就是好方法。所谓“分类”的这个“分”字,更强调全局性地把问题分解成固定的几个子类。 面向接口编程更合适用角色驱动来描述。角色并不存在只此一家的宇宙唯一分类标准。它是面向各个单一模块的需求的,全局设计上到底分多少类我根本不关心,只要给这个模块的需求用接口描述出来即可。 |
|
返回顶楼 | |
发表时间:2006-02-22
Readonly 写道 robbin 写道 好吧,那么我是web程序员,我从页面传一个id过来,要先load一下该Account,显示account的信息。那么请问:你提供给我什么API?是
account.getAccountById(id); 还是 accountService.getAccountById(id); 还是 accountDao.getAccountById(id); ? new Account(id); Account.get(id) |
|
返回顶楼 | |