前言: 奶奶的个熊,越写越长,只能连载了,以下代码可直接拷贝到swi-prolog运行 % Author: zhang3 % Date: 2011-3-29 :-dynamic(create). :-dynamic(from). :-dynamic(change). :-dynamic(to). :-dynamic(consists_of). :-dynamic(side). :-dynamic(value). :-dynamic(angle). :-dynamic(as). :-dynamic(set_width_of). :-dynamic(set_height_of). :-dynamic(set_side_length_of). :-dynamic(is_). :-dynamic(isa). :-dynamic(width_of). :-dynamic(height_of). :-dynamic(side_length_of). :- op(901,fy,(create)). :- op(901,xfy,(from)). :- op(901,fy,(change)). :- op(901,xfy,(to)). :- op(901,xfy,(consists_of)). :- op(902,fy, (side)). :- op(901,xfy, (value)). :- op(901,xfy, (length)). :- op(901,fy, (angle)). :- op(901,xfy, (as)). :- op(901,fy, (set_width_of)). :- op(901,fy, (set_height_of)). :- op(901,fy, (set_side_length_of)). :- op(901,xfy, (is_)). :- op(901,xfy, (isa)). :- op(901,fy,(width_of)). :- op(901,fy,(height_of)). :- op(901,fy, (side_length_of)). /* */ %上面为一些准备工作,定义了一些操作符,以便让prolog代码看起来更像自然语言。 %这里是非逻辑的部分,改变当前的事实,前面是旧的事实,后面是新的要替换的事实。 change Old to New:- retract(Old),assert(New),fail. change Old to New:- !. /* 世界是由事实组成,而不是由实体而组成。 按照常识性的理解,很容易认为,世界由各种实在的物体所组成,而实在具有各种性质和相 互作用,作为常识这是可靠的,而作为哲学方法论实在是粗鄙不堪的,连古希腊的哲学家们 都早已超越这种认识,19世纪初更是开始了哲学的语言学革命,分析哲学成为当今哲学的主 流,但OO的设计者们却毫无警觉的将其引入到软件设计中,让整个软件设计方法学走上歧途。 概念和实体 parallelogram(平行四边形),rectangle(长方形),square(正方形)这都是理念世界中的存在。 当我们说parallelogram有四条边的时候,我们不是在说parallelogram真的像一个摆在面前的苹果 那样真实存在着,而且有果肉,果核,而是说在说当一个parallelogram类型的Object被创建之 后,世界状态的变化。 因此我们可以写出这样一个创建类型为parallelogram的对象的命令句: */ create Object from parallelogram:- assert(Object consists_of (side 1) value 10), assert(Object consists_of (side 2) value 20), assert(Object consists_of (side 3) value 10), assert(Object consists_of (side 4) value 20), assert(Object consists_of (angle 1) value 30). /* 在创建时,在逻辑数据库(也就是世界状态)中,插入了几条事实。 简单说来,事实就是一个陈述句,比如shape1 consists_of (side 1) value 10。这句话的 含义就是这句话本身,至于shape1到底是什么,无需关心,因为这个问题本身就没有意义。 在创建一个名为shape1,类型为parallelogram的对象之后,世界的状态就变了,这个变化不是 物体某个属性的变化,而是事实的变化。在调用之前,世界的状态是不包括关于shape1的事实 的。 在进行这样的调用之后(有swi-prolog环境的可以在命令行中输入下面的句子): create shape1 from parallelogram. 世界状态中就多了这样几个事实: shape1 consists_of (side 1) value 10. shape1 consists_of (side 2) value 20. shape1 consists_of (side 3) value 10. shape1 consists_of (side 4) value 20. shape1 consists_of (angle 1) value 30. 通过查询我们可以知道这一点: ?-shape1 consists_of X value Value. X = side 1 Value = 10 ; X = side 2 Value = 20 ; X = side 3 Value = 10 ; X = side 4 Value = 20 ; X = angle 1 Value = 30 ; */ /* 增加一个类型判断,用于判断一个对象是否是parallelogram */ Object isa parallelogram:- Object consists_of (side 1) value S1, Object consists_of (side 2) value S2, Object consists_of (side 3) value S3, Object consists_of (side 4) value S4, Object consists_of (angle 1) value A, S1=S3,S2=S4. /* 现在要说到rectangle了,不要把关注点放在长方形具有什么性质之内的本体问题上,而是要 问,当一个名为shape1的rectangle被创建之后,世界中多了些什么事实(陈述句),可以做 一些什么关于shape1的事情? */ /* 首先,类似这样的四条事实会成立:shape1 consists_of (Side X) value Y. 其次,应该有:shape1 consists_of (angle 1) value 90. 这样我们可以写下创建rectangle的方法,这个方法是首先创建一个parallelogram,然后改变 关于角度的事实,将其改为90度。 */ create Object from rectangle:- create Object from parallelogram, change (Object consists_of (angle 1) value A1) to (Object consists_of (angle 1) value 90). /* 然后是判断Object是否是一个rectangle的谓词。在动态语言中一般是不用管的,但这个演示 还是照顾了一下静态的需要。条件是:两条边对应相等,角度为90度。 */ Object isa rectangle:- Object consists_of (side 1) value S1, Object consists_of (side 2) value S2, Object consists_of (side 3) value S3, Object consists_of (side 4) value S4, Object consists_of (angle 1) value A1, S1=S3,S2=S4,S1<>S2,A1=90,!. /* 现在做一个小小的测试,先创建一个rectangle ?-create shape1 from rectangle. Yes. 然后检查它是否是rectangle,在这里仍然要强调指出,这里的 Object isa Class句式,其 效果和任何一句表示事实的语句一样,并不因为是isa就有任何特殊性,听起来很绕,我们最 好还是说:shape1 isa rectangle成立,而不是直接说 shape1 is a rectangle,以避免又 落入常识的陷阱。 从这里又可以看出,在OO理论中作为重点的Type,在这种新的方法论下毫无特殊之处。 现在来检查这个事实是否成立 ?- shape1 isa rectangle. Yes. 再检查另外一个事实 ?- shape1 isa parallelogram. Yes. 同样也成立,这毫不奇怪。 这算是继承吗? 它不是OO概念中的继承,但它同样可以完成继承所完成的功能,而且,采用了这种方法,我们 将毫无困难的越过OO继承给我们造成的障碍,处理比单继承和多重继承复杂得多的现实情况。 */ /* rectangle的宽和高 毫无悬念,我又要强调:世界是由事实而非实体所构成,所以真正的问题是,如何才能进行 这样的命令和查询: 命令: ?- set_width_of Object to Width. ?- set_height_of Object to Height. 查询: ?- width_of Object is_ Width. ?- height_of Object is_ Height. 按照数学上的定义,Object的宽是长边的长度,高是短边的长度。因此我们有如下规则: 注:下面用的is_是因为is已经被prolog作为内部谓词,也就是保留字了。 */ %设置宽度就是改变关于两条长边的事实。 set_width_of Object to Width:- Object isa rectangle, width_of Object is_ OldWidth, change (Object consists_of (side X) value OldWidth) to (Object consists_of (side X) value Width). %设置高度就是改变关于两条短边的事实。 set_height_of Object to Height:- Object isa rectangle, height_of Object is_ OldHeight, change (Object consists_of (side X) value OldHeight) to (Object consists_of (side X) value Height). %宽度是较长的那条边。 width_of Object is_ Width :- Object isa rectangle, Object consists_of (side 1) value S1, Object consists_of (side 2) value S2, Width is max(S1,S2),!. %高度是较短的那条边 height_of Object is_ Height :- Object isa rectangle, Object consists_of (side 1) value S1, Object consists_of (side 2) value S2, Height is min(S1,S2),!. /* 同理我们可以创建square */ %创建square的方法是,创建一个rectangle,把所有边都变成10这个默认值。 create Object from square:- create Object from rectangle, change (Object consists_of (side X) value V) to (Object consists_of (side X) value 10). %isa判断 Object isa square:- Object consists_of (side 1) value S1, Object consists_of (side 2) value S2, Object consists_of (side 3) value S3, Object consists_of (side 4) value S4, Object consists_of (angle 1) value A1, S1=S2,S2=S3,S3=S4,A1=90. %查询边长 side_length_of Object is_ Length:- Object isa square, Object consists_of (side 1) value Length. %设置边长 set_side_length_of Object to Length:- Object isa square, change (Object consists_of (side X) value _) to (Object consists_of (side X) value Length). /* 现在可以开始做一些测试,看看这样的代码究竟带来了什么? 测试1: ?-create shape1 from square. Yes. ?-shape1 isa rectangle. No. ?-shape1 isa parallelogram. Yes. 测试2: ?-create shape2 from rectangle. Yes. ?-height_of shape2 is_ Height,set_width_of shape2 to Height. Yes. ?-shape2 isa rectangle. No. ?-shape2 isa square. Yes. 测试3: ?-create shape3 from rectangle. Yes. ?-shape3 isa parallelogram. Yes. 根据测试结果,按照我们常识性的说法(如果长方形特指对应边不相等的直角平行四边形) 测试1 如果shape是正方形,那么shape不是长方形,但shape是平行四边形。 测试2 假定shape是长方形,那么如果把shape的宽度设置成高度,那么shape就会变成正方形。 测试3 如果shape是长方形,那么它是平行四边形。 按照传统的OO理论,似乎应该将平行四边形设置为基类,而正方形和长方形都继承这个基类, 这样可以满足1和3,但对于2,就诡异了,因为你可以改变一个长方形的高和宽将其转变 为一个正方形,但不能通过设置正方形的边长将其转变为长方形。 */
【连载】对象和类是一种幻觉 --- 面向对象的解构之二
http://www.douban.com/note/142620392/
我们把下面两段代码拿过来比较一下: 1 创建对rectangle对象 create Object from rectangle:- create Object from parallelogram, change (Object consists_of (angle 1) value A1) to (Object consists_of (angle 1) value 90). 2 设置rectangle对象的宽度 set_width_of Object to Width:- Object isa rectangle, width_of Object is_ OldWidth, change (Object consists_of (side X) value OldWidth) to (Object consists_of (side X) value Width). 能看出这两段代码有什么不同吗?如果你把create替换为另一个没有明确含义的单词试试看? verb Object of rectangle:- verb Object of parallelogram, change (Object consists_of (angle 1) value A1) to (Object consists_of (angle 1) value 90). 显然,我们找不到任何有意义的差别,那么,在下面两个命令句中: ?-create shape1 from rectangle. Yes. ?-set_width_of shape1 to 100. Yes. 这些名词:create,shape1,from,ractangle,set_width_of,to,100 并没有体现出差别。 从这种角度来看,对象消失了,类也消失了,因为,对象和类本来就是一种幻觉。 那么,这种幻觉是怎么产生的,又因什么而根深蒂固? 为了回答这个问题,不得不扯得远一点。 先来看看陈述句和命令句的区别,想必已经注意到,这里存在着两种截然不同的句子, 第一种是陈述句,用于查询,而不会改变世界状态,例如width_of Object is_ Width. 第二种是命令句,用于执行操作,会改变世界状态,例如create shape1 from rectangel. ?-create shape1 from rectangle. Yes. 假定在有人类以前,就已经运行过很多次上述的代码,并生成了关于shape1,shape2,..., shape100的多条事实。 人类诞生之后,认识世界的唯一方法是,与这个世界互相影响,规范的说,这就是实验。 实验: ?-set_width_of shape1 to 100. Yes. 查询: ?-width_of shape1 is_ Width. Width = 100. 第一句就相当于实验过程,第二句是对实验结果的查询和检验。 这相当于,一个原始人今天把100颗骨棒埋到一个坑里,明年再挖出来一看,哇,还是100颗。 这种固定的联系引起了人的好奇,因此他会总结(归纳)道: set_width_of X shape1 to Width,width_of shape1 is_ Width. 这是一个复合句,包含了命令句和陈述句,将Width用很多数据来代替,这句话总是成立, 因而这个句子,就成为一个理论,具有了预测力和可错性。 (注:真正的,彻底的说明这个过程需要用到时态逻辑,prolog本身还缺乏原生的表达方式, 而且为了避免过于复杂,有的地方特意模糊带过,不做深究) 在大量的实验之后,往往会发现一些共性,例如出现一系列这样的理论: set_width_of X shape1 to Width,width_of shape1 is_ Width. set_width_of X shape2 to Width,width_of shape1 is_ Width. set_width_of X shape3 to Width,width_of shape1 is_ Width. 。。。 set_width_of X shape100 to Width,width_of shape1 is_ Width. 这样的理论虽然有用,但也是不能令人十分满意的,因为每个理论都只针对一种现象,就好 像原始人总结出: 1 埋N颗骨棒,明年挖出来依然是N颗 2 埋N颗石头,明年挖出来依然是N颗 3 埋N颗贝壳,明年挖出来依然是N颗 ... ... 我们还需要一个更简单的理论: 如果 X isa rectangle 那么 set_width_of X to Width,width_of X is_ Width. shape1 isa rectangle. shape2 isa rectangle. ... ... shape100 isa rectangle. 由于rectangle不仅仅可以set_width_of,还具有大量的事实成立,所以这个理论的发明, 会大大简化理论的描述长度,也就是增加了每个比特位的信息量。 从发明这个理论开始,对象和类的概念就逐渐从一系列的实验观察中产生了。 然而,在软件设计中,这种朴素的实体和类型的理论受到了严重的挑战,一些现象根本 无法用这种解释实体的理论来解释,因而,建立在朴素实在论基础上的OO设计方法学, 会受到一系列严重的挑战,为了避免这种冲突的产生,我们必须追根溯源,探索对象与类 幻觉的来源,构造,才能得到合理和可靠的解决方案。
一直很忙,今天一口气读完,确实非常棒。
我已经好久没有折腾OO了...
--
LIU
注:从这篇开始就有点乱了,显然一个尚未成体系的东西,我开始的要求过高了一点,从这篇开始,会抓住一些实际的实例进行分析,主要的例子是《设计模式》一书中所提炼出来的那些,偶尔会有一篇其他的或者特意构思的,比如这篇就是。
在RPG游戏中,人物携带物品在地图中走动,不同的物品具有不同的功能,如果就这么简单,那对物品的使用,就可以成为物品的一个方法,使用物品就调用这个方法好了。
很要命,不同的人物使用同一件物品的时候,效果也不一样。
这时候还要把这个方法放到物品对象中吗?
也许还可以,OO分析会开始非人类的胡言乱语:如果在方法调用中,加上user参数,那么物品知道是谁在使用它,于是就可以根据使用者的不同来进行不同的处理了。
卖锅的,物品居然活了,它成了一个精灵,知道谁在使用它。
好吧,这样是可以了。但问题是,做为一件物品,它为了根据使用者的不同来进行不同的处理,那么它就得知道使用者的类型,耦合就产生了,即使你又抽象出某种公共的接口,耦合依然存在,但处理上会稍微舒服一些。
有人试图改变这种耦合,把使用物品的方法加到了人物对象上,只不过是顾头不顾腚的做法,按下葫芦浮起瓢。这里的耦合不见了,会在另外一个地方冒出来,只不过,有经验的设计者会预先知道这一点并权衡不同方式的利弊,对于性质不同的系统,采用不同的处理方式。
如果再加上一种,那就是场景,比如地上打和地下使用不一样,在庄稼地和宫殿里使用也不一样,那又该如何?
OO分析的问题是,一开始就把这个问题搞错了,这个使用效果的方法哪个对象也不属于,它应该同时关联到人物,物品,和场景。
一般来说是这样的形式:Character use Item in Scene.
接下来就可以写具体的实现:
假定有一个物品item1,不论谁用,在哪用效果都一样,都是"boom",那么就这样写:
Character use Item1 in Scene:- write('boom').
再假定有一个物品item2,被type1类型的Character使用是"piapia1",被type2类型的Charater使用是"piapia2",但跟场景没关系:
Character use item2 in Scene:- Charater isa type1,write("piapia1").
Character use item2 in Scene:- Charater isa type2,write("piapia2").
再假定一个物品item3,使用效果跟人物无关,但在某个场景scene1下,被某个特殊类型type1的Charater使用时,有特殊的效果"hong",其他时候都是"puci"
Character use item3 in scene1:- Character isa type1,write("hong"),!.
Character use item3 in scene1:- write("hong").
最后再来一个,有个场景scene3下,有个type3的Character不论使用什么物品,都是"pong"的效果
Character use Item in scene3:- Character isa type3, write("pong").
够复杂了吧,这一系列的代码写起来,几乎就是一个表格,一个很直观的表示人物,物品,场景和使用效果的表格。
在具体的调用时,会根据调用行的模式匹配上上述表格的某一行或者某几行,然后执行相应的效果代码。
那这些方法到底属于哪个对象呢?
这已经成了一个多余的问题。【注】看起来是回到了过程式,但这是以模式匹配的方式来匹配到所需要调用的方法。
而且,如果抛开对象的迷信,看调用对象方法的形式:
Object.func(...)
如果把那个代表引用的点只看做一种语法形式,那这个Object也不具有任何特殊的地位,只是语法的一个组成部分而已。