【连载】面向对象的解构

24 views
Skip to first unread message

open audio

unread,
Mar 29, 2011, 9:11:47 PM3/29/11
to pongba

http://www.douban.com/note/142604730/


【连载】面向对象的解构--之一

2011-03-30 00:05:07
前言:
奶奶的个熊,越写越长,只能连载了,以下代码可直接拷贝到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,就诡异了,因为你可以改变一个长方形的高和宽将其转变
为一个正方形,但不能通过设置正方形的边长将其转变为长方形。
*/

open audio

unread,
Mar 29, 2011, 9:17:23 PM3/29/11
to pongba

【连载】对象和类是一种幻觉 --- 面向对象的解构之二

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设计方法学,
会受到一系列严重的挑战,为了避免这种冲突的产生,我们必须追根溯源,探索对象与类
幻觉的来源,构造,才能得到合理和可靠的解决方案。

Larry, LIU Xinyu

unread,
Apr 8, 2011, 6:03:53 AM4/8/11
to TopLanguage
Hi,

一直很忙,今天一口气读完,确实非常棒。
我已经好久没有折腾OO了...

--
LIU

open audio

unread,
Apr 8, 2011, 6:48:16 AM4/8/11
to pon...@googlegroups.com
还是那句话:知音难觅

后面的会更好。

open audio

unread,
Apr 8, 2011, 6:55:37 AM4/8/11
to pon...@googlegroups.com

http://www.douban.com/note/143944769/


【连载】面向对象的解构之三--游戏中的人物和物品

2011-04-06 17:12:59
注:从这篇开始就有点乱了,显然一个尚未成体系的东西,我开始的要求过高了一点,从这篇开始,会抓住一些实际的实例进行分析,主要的例子是《设计模式》一书中所提炼出来的那些,偶尔会有一篇其他的或者特意构思的,比如这篇就是。



在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也不具有任何特殊的地位,只是语法的一个组成部分而已。
Reply all
Reply to author
Forward
0 new messages