游戏开发论坛

 找回密码
 立即注册
搜索
查看: 17132|回复: 34

过程式编程的穷途末路之一

[复制链接]

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
发表于 2009-1-5 21:40:00 | 显示全部楼层 |阅读模式
本文将通过介绍几个月前的一个实际问题,来说明过程式编程的穷途末路之一。
(请注意这只是其中之一)

问题描述如下:

一颗树中,有若干节点,每个节点有 node_visible 属性(下简称NodeVis),表示这个节点是否可见。
一个节点的真正的可见性,取决于:
1) 父母的可见性
2) 自己的可见性
(即父母不可见该节点一定不可见;反之,则按这个节点本身的NodeVis)
方便起见,我们把一个节点真正的可见性称为 RealVis,下同。

  1. 这个问题从逻辑上说格外简单,实际上用 仿真语言 写的话只需要 几行 代码:
  2. node.real_visible =
  3.     if node.parent != null
  4.         node.parent.real_visible && node.node_visible
  5.     else
  6.         node.node_visible
复制代码

现在要把以上的系统封装成类库。用户需要知道每个节点的NodeVis和RealVis, 以及修改节点的NodeVis.
(注意RealVis没法直接修改,只能修改NodeVis)

取得节点的NodeVis很直接,直接返回即可。
取得节点的RealVis也很简单,我想这种过于基础的编程就不用说了。
(*注:频繁读取RealVis的话,应当避免重复计算,就好比不要每次都从头背诵99乘法表)

修改节点的NodeVis看似很直接,其实不然,比如我把节点A的NodeVis设为false那么它子孙的RealVis也会全部变成false,那么,用户当然需要知道,这些节点的RealVis的变化情况!
(此外,如果用户把某个节点从树中移除(即parent=null)那么这个节点及其子孙的RealVis也会全部变成false)

归纳一下可分为 输入 输出 两个部分. 即,用户修改节点,和用户获取节点的属性.

>> 输入:
主要可以有两种方式实现
A) 通过阻塞式函数调用直接修改节点.
B) 通过非阻塞的消息机制修改节点. (我认为这毫无必要,因为下文会提到输出是非阻塞的,这就可以了)

>> 输出:
主要可以有2方式实现(其实我自己全都用过)

A) 异步事件.
当节点的NodeVis被修改(或者从树中移除该节点)时,立刻计算出子孙的RealVis的变化,并产生相应的RealVisChanged事件以通知用户.注意:子孙的RealVis变化当然可以一次性算出而无需重复计算.
(在我的Event类中,采用非阻塞方式实现事件)

B) Update.
不需要产生任何RealVisChanged事件,而是用户自己在每个frame中去Update(或者仅在需要的时候去Update).

那么看一下利弊(包括编程难度,质量和速度)

A) 异步事件.
利(1) 非常严密,没有遗漏。因为无论何时,只要变动了某个节点,那么其他节点的RealVis的变化的事件就一定会被不漏地产生.
利(2) 子孙的RealVis变化当然可以一次性算出而无需重复计算.
弊(1) 编程难度较大,因为修改一个节点的NodeVis,从树中移除该节点,把一个节点添加到树中,都需要求出知道其他节点的RealVis的变化,也就是说需要写好几个算法,而且相互之间还要严密配合,不出差错,确保RealVisChanged事件的正确产生
弊(2) 效率问题很难说 如果这些节点的变动不频繁,那么此法的效率肯定高. 但如果是在比如游戏这种变化多端的系统中,那么,效率是很低下的。比如有100节点,游戏每frame需要变动100个节点,而每个节点都有一大堆子孙需要被结算,那么每frame的计算量是很大的,也有相当多的重复计算.

B) Update.
利(1) 编程难度较小,因为不需要考虑节点的NodeVis的变化对子孙的影响。只需直接编码即可。
利(2) 效率问题很好控制. 因为用户无论如何都是每frame都把每个节点的属性读取一次. 既不会非常慢,也不会非常快.
弊(1) 不严密. 如果用户在一个frame中多次变动某节点,那么这些变动无法一一告知用户,因为每frame只update一次。
弊(2) 取得节点的RealVis的算法. 用户每frame都要把每个节点的RealVis读取一次,如果不设法改进算法,就会造成一些重复计算,就好比每次都从头背诵99乘法表一样.

45

主题

1163

帖子

1165

积分

金牌会员

Rank: 6Rank: 6

积分
1165
发表于 2009-1-6 08:38:00 | 显示全部楼层

Re:过程式编程的穷途末路之一

你根本就不是一个程序员,现在我们公司里好多很厉害的人,都是和和气气的,没人像这样大段大段理论搬出来的,你越是想炫耀你所谓的“技术”,越是说明你的无知。回头是岸吧!

5

主题

972

帖子

975

积分

高级会员

Rank: 4

积分
975
发表于 2009-1-6 11:14:00 | 显示全部楼层

Re:过程式编程的穷途末路之一

他不是程序员,或者说不是一般的程序员。
其实这位lz说话也没有不和气,只是说了一大堆我们很难懂或者不关心的话而已。
这个世界确实是需要一些这种“怪人”的。他说的可能99%的都没有用,但那1%可能就是有所突破的。
当“怪人”是有风险的,就像赌博一样。

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
 楼主| 发表于 2009-1-6 13:07:00 | 显示全部楼层

Re:过程式编程的穷途末路之一

我已经在 baidu 知道 提问这个问题,想得到意见。。。
http://zhidao.baidu.com/question/81399999.html

45

主题

1163

帖子

1165

积分

金牌会员

Rank: 6Rank: 6

积分
1165
发表于 2009-1-6 13:11:00 | 显示全部楼层

Re:过程式编程的穷途末路之一

哎。。。。。再也不理你了,一片好心劝你,你尽然越走越错,大学本科都没考上,你以后一定会后诲的!

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
 楼主| 发表于 2009-1-6 13:13:00 | 显示全部楼层

Re:过程式编程的穷途末路之一

我用面向方程的伪代码来写这个程序:

node.real_visible =
    if node.parent != null
        node.parent.real_visible && node.node_visible
    else
        node.node_visible

搞定了,简单把?
但这么一段伪代码,用C++等代码去仿真的话,却相当难(问题在#1楼已写出)。

6

主题

40

帖子

58

积分

注册会员

Rank: 2

积分
58
发表于 2009-1-9 12:45:00 | 显示全部楼层

Re: 过程式编程的穷途末路之一

我个人认为这个问题没有讨论的必要,我们编写代码是为了实现某些需求。必然集合实际问题才能进行具体的分析与设计。文中提到的解决办法,但并没有提到实际的需求和需要使用到的地方,只是“想”设计一个封装成类库而已。

只有根据具体的需求,比如内存是否有严格要求,树变动的速度,是否需要实时通知节点变化,对效率的要求等等。一个完美的东西是不存在的,必然需要舍弃一些东西。

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
 楼主| 发表于 2009-1-9 19:07:00 | 显示全部楼层

Re: Re: 过程式编程的穷途末路之一

hmnes: Re: 过程式编程的穷途末路之一

我个人认为这个问题没有讨论的必要,我们编写代码是为了实现某些需求。必然集合实际问题才能进行具体的分析...


1
这就是我的实际问题啊,只不过是其中一小部分。

2
可以不讨论,可以不谈理论,不过我想看到完整的实现代码。没有代码,说什么都是虚的。

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
 楼主| 发表于 2009-1-9 19:46:00 | 显示全部楼层

Re: 过程式编程的穷途末路之一

看一下我的代码(部分)。只是设置一下visible就要这么麻烦
void CCore::SetWndNodeVisible(WND_HANDLE hwnd,Bool node)
{
    if(!hwnd)return;
    if(!(*hwnd))return;

    if( node == (*hwnd)->bNodeVisible ) return;

    if( IsWndVisibleReally(hwnd) )
    {

        INST_ASSERT( (*hwnd)->bNodeVisible );

        Array<WND_HANDLE> VisibleDescendants;
        _EnumVisibleDescendants(hwnd,VisibleDescendants);
        
        INST_ASSERT(False==node);
        (*hwnd)->bNodeVisible = node;

        INST_ASSERT( False == IsWndVisibleReally(hwnd) );

        if( m_hMouseCapture == hwnd )
        {
            (*m_hMouseCapture)->LostMouseCapture.PostEvent( this, SMART_NEW(CWndEventArgs,(m_hMouseCapture)) );
            m_hMouseCapture = null;
        }
        (*hwnd)->EnterFlag = MEF_UNINITED;
        (*hwnd)->GotInvisible.PostEvent( this, SMART_NEW(CWndEventArgs,(hwnd)) );

        for(Int32 i=0; i<VisibleDescendants.Count(); i++)
        {
            WND_HANDLE tmp = VisibleDescendants.Get(i);
            INST_ASSERT(tmp);
            INST_ASSERT( *tmp );
            INST_ASSERT( False == IsWndVisibleReally(tmp) );
            if( m_hMouseCapture == tmp )
            {
                (*m_hMouseCapture)->LostMouseCapture.PostEvent( this, SMART_NEW(CWndEventArgs,(m_hMouseCapture)) );
                m_hMouseCapture = null;
            }
            (*tmp)->EnterFlag = MEF_UNINITED;
            (*tmp)->GotInvisible.PostEvent( this, SMART_NEW(CWndEventArgs,(tmp)) );
        }

    }
    else // False == IsWndVisibleReally(hwnd)
    {

        if( True == (*hwnd)->bNodeVisible )
        {
            INST_ASSERT(False==node);
            (*hwnd)->bNodeVisible = node;
            INST_ASSERT( False == IsWndVisibleReally(hwnd) );
            return;
        }
        else // False==(*hwnd)->bNodeVisible
        {

            INST_ASSERT(True==node);
            (*hwnd)->bNodeVisible = node;

            if( False == IsWndVisibleReally(hwnd) ) return;

            Array<WND_HANDLE> VisibleDescendants;
            _EnumVisibleDescendants(hwnd,VisibleDescendants);

            (*hwnd)->GotVisible.PostEvent( this, SMART_NEW(CWndEventArgs,(hwnd)) );

            for(Int32 i=0; i<VisibleDescendants.Count(); i++)
            {
                WND_HANDLE tmp = VisibleDescendants.Get(i);
                INST_ASSERT(tmp);
                INST_ASSERT( *tmp );
                INST_ASSERT( True == IsWndVisibleReally(tmp) );
                (*tmp)->GotVisible.PostEvent( this, SMART_NEW(CWndEventArgs,(tmp)) );
            }

        }
    }
}
void CCore::_EnumVisibleDescendants(WND_HANDLE parent,Array<WND_HANDLE> &out)const
{
    INST_ASSERT(parent);
    INST_ASSERT( *parent );

    INST_ASSERT( IsWndVisibleReally(parent) );

    for(Int32 i = 0; i < (*parent)->hChildren.Count(); i++)
    {
        WND_HANDLE tmp = (*parent)->hChildren.Get(i);
        if( (*tmp)->bNodeVisible )
        {
            out.AddEnd(tmp);
            _EnumVisibleDescendants(tmp,out);
        }
    }
}

45

主题

1163

帖子

1165

积分

金牌会员

Rank: 6Rank: 6

积分
1165
发表于 2009-1-9 19:53:00 | 显示全部楼层

Re:过程式编程的穷途末路之一

哎。。。。。。。。。。我总是把复杂的东西搞简单,而你是把简单的东西搞复杂
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2025-6-6 12:15

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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