游戏开发论坛

 找回密码
 立即注册
搜索
查看: 2266|回复: 8

面向对象的优秀之处

[复制链接]

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
发表于 2008-6-6 18:22:00 | 显示全部楼层 |阅读模式
面向对象的优秀之处


本文提要

1 论述面向对象的好处和问题
2 网游的 client 和 server 的问题(他们做些什么?)
3 (在细节上)正确区分“面向对象思想”和“面向过程思想”
4 但是!从大框架上,一切都是应该用“面向对象思想”!
5 不过...作为一个整体来分析,本质上,程序都是面向过程的
6 “本文有自相矛盾的地方!?”----不,我没有!


引子

最近我采用的架构是,AI 和 核心逻辑分开。AI通过和人类玩家一样的接口与逻辑交互。
这对于网络游戏同样适用,每个client(人类玩家)都要和server的核心逻辑交互。
而client, AI 他们不能自己决定一些东西的数值,必须通过server的核心逻辑。
(上述各个模块间的交互,分为“直接读取”“消息通知”,而不允许直接进行非const函数调用)
以上是大框架。而在细节方面,近期我基本上只考虑了server的核心逻辑。

昨天有位朋友说了自己的见解:“游戏,就是一些能够自主行为的对象,加上制约这些对象的规律”
自主行为,比如说“我要吃饭”;规律,比如说“不吃饭不能生存”

此外,请大家注意思维上的“层次化”。如果把这世界看作一个整体,那么万物都要受到规律的制约;
然而进一步细化,则可以分为“意识世界”(能动的)和“外界世界”(受外界的自然规律制约)


下面进入正题。
开门见山,因为大家都知道 OO ,所以我先给出本人构思的一种
“纯粹面向对象模型”(稍后还会修改)


class CPureObjectABC : public IProcess  //如果用win32多线程就不需要继承IProcess
{
private:  // 私有成员
    list<YourMsg> m_listMsgs;
    Type m_aaa;
    Type m_bbb;
public:   // 共有成员
    Type get_XXX()const;  // 询问属性
    void PostMessage( YourMsg msg );  // 通知消息
    void DoSomething();  // 不推荐
public:   // 从IProcess继承而来(不是必需的)
    virtual void Update( UInt32 dt );  // 如果用win32多线程就不需要这个
};

这个模型的意图,我想很直接,就是定义了一个可以并行做事情的“对象”。对象之间可以随便取得属性,
但是,决不允许相互 直接 设置(Write) 属性,而只能通过 PostMessage() 进行通知,或者函数调用。
不过,我不推荐用函数调用,因为主调对象必须等待被调函数返回后才能继续工作,而程序员很容易忘记这一点。

楼主想通过这个,说明什么?

我看到一篇文章说,OO是为了解决“软件危机”而发明出来的。“对象”的概念,正如团队中的每个程序员,
大家可以相互交流,相互配合。(比如,“我看看别人在做什么,然后决定我自己该做什么”)
但我还看到一文章说,“C++, java不是纯粹的 OO, 还依赖于过程”。

是啊,正因为“不纯粹的 OO”的思想(我认为是思想),给我们引来过许多有趣的问题和故事,
所以,我想说的是,举一个纯粹的OO模型的例子。注意,只是例子!

(gc, refcount之类的东西因为“比较底层”所以不在我们的讨论范围之内。)

现在我们给出了一个“纯粹的OO”。那么,如何编写程序?好处在哪里?

----这和现实生活中人与人的协作,是一样的。你要运作一个团队,就必须相互合作。
换来的好处是,你可以进行“分工”,即每个人和别人配合,完成自己的工作。
历史上,OO将人们从“软件危机”中挽救了出来,正是因为OO,可以进行“分工”即“模块化”。
OO 的伟大思想,少不了“人”这个概念。没有“人”就没有 OO, 没有“人与人的合作”,OO就不能工作。

也就是说,编写OO程序,必须要尽可能考虑:别的对象可能会做什么?那么我应该如何应对?
----即,要拥有“与人合作”的 思 想 !而不应当鲁莽地做事,也不能 假 定 “别人肯定会听我的”
----虽然也可以直接调用public函数(不推荐),但是,不要认为,这个函数“一定会听我的话”。


问题的出现

上面说了,OO运用的是“人与人协作”的思想。换言之,就是“人治”。
这样无法解决某些问题,特别是在游戏中,比如,2个角色相互厮杀,他们的受伤计算。
在真实的人类社会中,如果“动真格的”那么“受不受伤”并不是2个人之间的相互合作,
而是由“自然规律”决定的!

于是,纯粹的OO,就给我们的编程,带来了一定的问题!我们来看一下如何解决:

(1) “不动真格的,这只是游戏”。
由于一切都是“人文主义”,所以,姑且把2个角色的PK,
当作是一场玩闹,不动真格的,2个角色“相互配合”即可。但这种设计模式,要求我们的程序员,
能够高度合作,稍有差池,游戏的规则,就不能够精确保证。

(2)“请一个裁判,或者老板”。
假设2个角色的PK,是一场“表演赛”。而他们,必须听命于“裁判”“老板”。
首先,作为员工,肯定要和老板合作,否则就回家去了。而PK双方,不必合作,他们直接听命于“老板”即可。
但这种设计模式,要求 1 员工必须要能和老板合作。 2 老板自己不能出错。

(3)使用C++, java等“不纯粹的OO”。
这种模式有时候是优秀的,有时候是糟糕的。比如说,“我砍你,我可以直接减少你的 HP。”
但也可以这样:你被发现我砍,你减少自己的 HP”。。。
根本原因在于(后面详解),“OO的概念不纯粹,和面向过程没有区分开”。

(4)不使用 OO !?
既然我们发现,上述(1)(2)解决起来不方便,上述(3)是好坏参半,那么,我们就不要 OO !
不要OO,就免去了OO的麻烦事,保证了一些游戏规则的精确化,绝对化。


我们继续往下分析。。。
在文章开头我说了“游戏,就是一些能够自主行为的对象,加上制约这些对象的规律”
所谓“自主行为的对象”,当然我们用 (纯粹的) OO 去实现即可。
而“制约这些对象的规律”,是 OO 之外的事情,我推荐,用面向过程最好不过了。

“规律如何制约对象?”
对象之间的交互,通过“get_XXX()const”还有“PostMessage”,这是人与人之间的“友好的”交互。
而,“自然规律”,是不会和你“讨价还价”的!说1就是1,说2就是2,由不得你!

换言之,“自然规律”,不会通过get_XXX询问你的属性值,也不可能 PostMessage 通知你。
更不可能调用你的一个函数,等你处理完返回。(俗话说“水火无情”就是这个道理)

那么,我们需要将对象的一部分数据成员,直接公开给“自然规律”。
而自然规律,使用面向对象编码,直接操作这些数据。

那么,我们修改一下对象的代码:

class CPureObjectABC_2 : public IProcess  //如果用win32多线程就不需要继承IProcess
{
law:    // 想象的关键字
    Type m_bbb;  //  直接受自然规律制约的成员变量

private:  // 私有成员
    list<YourMsg> m_listMsgs;
    Type m_aaa;
public:   // 共有成员
    Type get_XXX()const;  // 询问属性
    void PostMessage( YourMsg msg );  // 通知消息
    void DoSomething();  // 不推荐
public:   // 从IProcess继承而来(不是必需的)
    virtual void Update( UInt32 dt );  // 如果用win32多线程就不需要这个
};

我用了一个想象的“law”关键字。即受外界自然规律制约的变量。你可以自由实现。

“自然规律”怎么编码??
----用面向过程 思想 即可,具体方法,如果是游戏逻辑的话,可以参考我的“浅谈游戏逻辑”一文。

但上面的这种模式,还存在一个小问题就是,“受自然规律制约的成员变量”能够由我自己随便修改??
在应用软件开发中,这或许不算什么问题,因为软件本来就不是什么“真实的”东西。

而在游戏开发中,比如你受伤了,这是“规律”,你还能说改就改??......我们看看下面的这种模型。


class CPureObjectABC_Matter : public IProcess  //如果用win32多线程就不需要继承IProcess
{
    friend class CLaw;  // 自然规律 - 虽然是“class”但完全是面向过程思想
private:    // 想象的关键字
    Type m_bbb;  //  直接受自然规律制约的成员变量。
public:
    Type get_BBB()const;
};

class CPureObjectABC_Mind : public IProcess  //如果用win32多线程就不需要继承IProcess
{
private:
    CPureObjectABC_Matter *matter;
private:  // 私有成员
    list<YourMsg> m_listMsgs;
    Type m_aaa;
public:   // 共有成员
    Type get_XXX()const;  // 询问属性
    void PostMessage( YourMsg msg );  // 通知消息
    void DoSomething();  // 不推荐
public:   // 从IProcess继承而来(不是必需的)
    virtual void Update( UInt32 dt );  // 如果用win32多线程就不需要这个
};

上面给出的只是基本原理,具体怎么编码因人而异,为了简单,我就不多废话了

这种模型,很类似于我说的“AI 和逻辑分离”的架构。因为 AI 就是“mind”,客户端也是“mind”;
而server的核心逻辑,就是一个绝对的“物质世界”以及“规则”。

这种模型,也更像是,“PK”,作为人类或AI,可以有“自由意志”,
然后,能动地,去操纵“自己的身体”以及“武器”,但都要受到“规律”的制约。
(注意了阿!自己的身体,也属于外界物质世界,因为你不可能直接控制你的 每 个 细 胞 的 生 长 !)


还是那句话,“规律”具体编码,本文不讨论。
...那么,从大框架上来看,我们不妨这么认为:

“规律”也是一个有自由意志的“对象”。人要和规律“合作”,了解规律,克服困难。
换言之,拟人化地把“老天爷”当成一个“人”。

人啊,要在这世界上生存,就必须和大自然“合作”,不是吗?


不过话虽如此,以唯物论观点,将程序(client + server)作为一个整体来分析,
他们本质上都是“面向过程思想”。。。毕竟,CPU 不懂什么“面向对象”。。。


“发现楼主有严重的自相矛盾!?”
“楼主说,在 OO 模式下,(2)“请一个裁判,或者老板”解决起来不方便,就是说不要面向对象,
而后面又说,从大框架来看,<自然规律>也是一个`对象`,这不矛盾吗?”

不矛盾,在 OO 模式下,处理 相互 PK 问题,“定义一个老板对象”相对好一些,但还是不太方便。
你怎么确信,员工肯定和老板合作呢?

就好比,允许client拥有游戏逻辑,那么,如何和server正确合作?还有,不担心外挂问题吗?
虽然不是“点对点”游戏,想必来说好一些,这没错,但是,还是有一些问题的,不是吗?
----所以,最好让server处理所有游戏逻辑。

类似地,应该将“员工的肉体”转移到“老板”中去,即,“老板”进化为“上帝”。
既然员工不存在肉体,那么,“上帝老板”这个“对象”,就代表了“绝对的规则”,
而“上帝老板”的 内 部 呢(包含所有员工的肉体!而不仅仅是老板自己!),
采用“面向过程思想”来编写。
----这也就是我说的“某些情况下,不要用 OO,而要用面向过程”...游戏核心逻辑还是比较庞大的体系,
研究如何用面向过程去实现它,则是本外之外的广大话题了。。。
说不定,一个上帝忙不过来,内部要分几个上帝一起合作呢(但不能分太多!否则面向过程的“权威”何在?)

7

主题

438

帖子

438

积分

中级会员

Rank: 3Rank: 3

积分
438
发表于 2008-6-6 19:56:00 | 显示全部楼层

Re:面向对象的优秀之处

写这么多纯属多余。
其实也就是说,面向对象和函数式等多个编程范式要相互补充。

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
 楼主| 发表于 2008-6-6 20:19:00 | 显示全部楼层

Re: Re:面向对象的优秀之处

justlikethewind: Re:面向对象的优秀之处

写这么多纯属多余。
其实也就是说,面向对象和函数式等多个编程范式要相互补充。


误会了,误会了。
我曾经写过“OO带来的陷阱”“浅谈游戏逻辑”
一直认为 OO 是坏东西。。。现在想,正确认识一下。


您说的“相互补充”是很对的。
但是,要注意并不是说“相互混淆”,即,“怎么相互补充?”


我还是那句话,“自主行为”要用 OO,
“规律,定律”用 面向过程,
或者更不如用 LISP,F#这样的函数式。


感谢您的批评指点

7

主题

438

帖子

438

积分

中级会员

Rank: 3Rank: 3

积分
438
发表于 2008-6-6 20:31:00 | 显示全部楼层

Re:面向对象的优秀之处

说起来,其实能愿意写这么多和其他朋友分享自己的心得也是一件了不起的事情。

所以……

お世?になりました。
お疲れさまでした。

64

主题

272

帖子

272

积分

中级会员

Rank: 3Rank: 3

积分
272
发表于 2008-6-6 23:24:00 | 显示全部楼层

Re:面向对象的优秀之处

"正是因为OO,可以进行“分工”即“模块化”。"这个貌似面向过程的时候就已经是把程序模块化的,我觉得OO能解决软件危机的最大原因是OO使得程序员可以按照人类思考的方式去编程而不是按计算机的思考方式去编程。
还有楼主说的“C++, java等“不纯粹的OO””我觉得很多人这样说的原因是任何计算机语言最后编译成机器语言都始终还是变回面向过程了。
另外“上面说了,OO运用的是“人与人协作”的思想。换言之,就是“人治”。
这样无法解决某些问题,特别是在游戏中,比如,2个角色相互厮杀,他们的受伤计算。
在真实的人类社会中,如果“动真格的”那么“受不受伤”并不是2个人之间的相互合作,”看到这里我感觉楼主好像写的有点混乱“人与人协作”是指开发人员协作,“2个角色相互厮杀”是指两个类交互吧?

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
 楼主| 发表于 2008-6-7 06:32:00 | 显示全部楼层

Re: Re:面向对象的优秀之处

梁某: Re:面向对象的优秀之处

"正是因为OO,可以进行“分工”即“模块化”。"这个貌似面向过程的时候就已经是把程序模块化的,...



"正是因为OO,可以进行“分工”即“模块化”。"这个貌似面向过程的时候就已经是把程序模块化的,我觉得OO能解决软件危机的最大原因是OO使得程序员可以按照人类思考的方式去编程而不是按计算机的思考方式去编程。

> 人的思考与相互间的合作,即“人治”。而一些东西,用“人治”是不准确地,
甚至是繁琐的不是吗?正因为如此,上帝是唯一公平的,绝对的。

还有楼主说的“C++, java等“不纯粹的OO””我觉得很多人这样说的原因是任何计算机语言最后编译成机器语言都始终还是变回面向过程了。

> 话说我听说过“smalltalk”这样的“纯粹的OO”,不过,根本不了解。

另外“上面说了,OO运用的是“人与人协作”的思想。换言之,就是“人治”。
这样无法解决某些问题,特别是在游戏中,比如,2个角色相互厮杀,他们的受伤计算。
在真实的人类社会中,如果“动真格的”那么“受不受伤”并不是2个人之间的相互合作,”看到这里我感觉楼主好像写的有点混乱“人与人协作”是指开发人员协作,“2个角色相互厮杀”是指两个类交互吧?

> 你说的没错,“受不受伤”并不是2个人之间的相互合作,所以,不应该用面向对象来处理。(如果硬是要用OO,那么就只能作为“2人合作”来处理)

> 不应该是2个类的交互,角色的相互厮杀,其受伤状况等,乃是“规律”,
而通过角色之间的相互合作,保证这种“规律”是比较难的。
即,需要一个绝对的“权威”。既然是绝对权威,那么,
这个“绝对权威”内部不应该再分成好几个类,
即,通过面向过程思想来保证“绝对权威”。

64

主题

272

帖子

272

积分

中级会员

Rank: 3Rank: 3

积分
272
发表于 2008-6-7 10:47:00 | 显示全部楼层

Re:面向对象的优秀之处

面向对象的四大特征是封装、抽象、继承和多态,据我所知只要满足这四点的都可以称为面向对象语言。
(所以像C++这样可以抽离出来纯C部分和纯面向对象部分)
这样的话单从00思想来考虑,两人PK不应该用面向对象来处理就不是很正确了。

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
 楼主| 发表于 2008-6-7 17:54:00 | 显示全部楼层

Re: Re:面向对象的优秀之处

梁某: Re:面向对象的优秀之处

面向对象的四大特征是封装、抽象、继承和多态,据我所知只要满足这四点的都可以称为面向对象语言。
(所以...


单机游戏:可以用OO,但是麻烦
网络游戏:游戏规则应当由server处理,即2个角色之外的“权威”

另外据我所知,
windows内核,是面向过程的。。。

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
 楼主| 发表于 2008-6-7 18:20:00 | 显示全部楼层

Re:面向对象的优秀之处

我搜索了一下

Smalltalk语言简介_学习空间所有Smalltalk 代码由发送到对象的消息链组成。甚至编程环境自身也是在这种表现方法(metaphor)下设计的。大量的预先定义的类共同的导致了这个系统惊人的功能。 ...

Smalltalk中最重要的一个是 message expression

实际上最初我极力反对在OO中使用“方法调用”的,这会造成一些麻烦。但有的朋友不这么认为。
于是我想了一下,在message模式下,如果A对象等待B对象,那么,这就 相 当 于 方法调用。
......最后我在上文的OO模型中,额外允许了一个 不 推 荐 的“方法调用”。

我不是说不能这样用!但程序员千万不能忘记!----当B对象的方法执行完成后,才会继续执行A对象的代码
。。。可惜,包括我在内,大多数程序员经常忘记这一点。

刚才说了,在message模式下,你仍然能够 模拟 “方法调用”的 效 果 。
但是,这 不会 使你犯错误!因为,要做到这点,需要好几行代码,而且,你自己写的代码会告诉你,
“A对象等待B对象的某个动作”
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-22 08:02

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表