软件代码设计零碎杂想-5

来源:互联网 发布:知乎我,一个人生活epub 编辑:程序博客网 时间:2024/04/28 11:45

本节讨论下数据流转方式。

一般情况下,所谓对象,都有状态信息的,这些状态,其实就是对象的数据。如果一个对象完全不与其他对象发生关系,基本上是很少见的,一般的函数都需要参数。今天这里不讨论关于状态如何变化的问题。

那么两个对象之间如何发送关系,对象A需要对象B的数据,有哪些方法可以完成?这些都是在进行设计的时候,需要了解的。先看例子:

class BTDownloader
{
    double rate,speed,downloaded;

    ....
};

 

一个BT下载者,内部保存了下载速率,下载速度,已下载的字节数。如何进行BT下载,就放一边了。现在,需要将BT下载的状态打印出来,该怎么做?

很显然的,一个对象的数据,被对象外的函数需要,有且仅有两个方法:主动推,被动拉,除此之外,别无它法。

主动推,就是当对象数据变化的时候,它需要向接受者发送。被动拉,则是由需要数据的函数发起要数据的动作。

一般的情况,都是被动拉是情况。如下:

class BTDownloader
{
    double rate,speed,downloaded;
public:
    double  getRate(){ return rate;}
    double    getSpeed() { return speed;}
    double    getDownloaded() { return downloaded;}
    ....
};

要用到被动拉的情况,则需要数据的函数必须拥有该对象才能使用该对象的公共函数,如下:

void xxx()
{
    //某处定义了    BTDownloader d
    d->getRate();
}

也就说,被动拉的方式,对于对象来说,是最简单方式,对于BTDownloader类来说,它完全不依赖另外的类,它只需要专心致志的做好自己的工作就行,至于谁向它要数据,它不关心,何时要数据,它不关心,如何表现这些数据,它更不关心。这些,都是它的客户需要思考的。如果要将下载状态显示在窗口界面里,那需要用到一个定时器,定时的询问数据,然后显示出来。

这里,对象的客户工作量稍微大点,如何将对象进行传递,在哪里创建该对象,在哪里初始化对象,在哪里增加下载任务而又在哪里查询状态,这些事情都是需要考虑的。主要的是各部分需要对象的时候,怎么能得到对象的问题。

再说说另一个情况,主动推。

主动推,是在数据有变化的时候,向接受者推数据,这里,对象就要依赖一个接受者类了。接受者接口类到底该如何设计,其实也没有定法,简单的,如下:

struct CReciver
{
public:
    void rate(double r)
    { printf("/nnew rate %f",r);}
    void speed(double s)
    {printf....}
    void downlaoded(double d)

    { printf ....}
};

这个类怎么用?很简单:

BTDownlaoder::OnDownlaod(...)
{
    //计算得到rate,speed,downloaded,现在需要把数据推出去
    //必须有 CReciver* reciver的一个指针

    reciver->rate( newrate);
    reciver->speed( newspeed);
    reciver->downloaded( newdownloaded);
}

如此一来,BTDownloader就要保存接受者的指针.为什么要保存接受者指针呢?不保存不可以吗?不保存,那数据变化的时候,怎么推出去呢?

既然要保存这些指针,那就要考虑这些指针是怎么来的。显然不是自己new出来的(为什么),而必须是对象的客户new出来后作为参数传递进来的。如此就有

class BTDownlaoder
{...
    void add_reciver(CReciver* r)
    {
        m_reciver=r;        
    }
};

具体到底是用单独的函数添加,还是在构造函数里传递,都无关紧要了。考虑下add_reciver是怎么做的?

void xxxx(...)
{

    //得到BTDownloader* d指针

    //得到CReciver* c指针

    d->add_reciver(c);   
}

一般的做法,当然是把这两个指针当作类成员变量来处理了。不过这样设计合适不合适再说了。

类似这样的设计好不好?BTDownloader不仅依赖一个CReciver类,而且要暴露一个函数来添加reciver.BTDownloader的客户不仅要熟悉BTDownloader,还要熟悉CReciver,还要为两个对象的生存周期考虑,还要考虑在不同的地方如何得到这两个类实例,我觉得实在有点麻烦。

如果对下载的状态的表现形式有变化,比如,需要在一个列表窗口中显示出来变化情况,那CReciver类就要修改了。嗯,修改方式如下:

class CReciver
{

int method;

HWND listHwn;
public:

 CReicer(int method);

 void setHwnd(h){ listHwnd=h}

 void rate(double r)

    if(method==1)

        printf("/nnew rate %f",r);  

    else if(method == 2)
    {
        postmessage(listHwnd,....);
    }

}

 

void speed(double s)
{
    if(method == 1)
        printf...
    else if ( method == 2)
    {
        postmessage(listHwnd,....);
    }
}
};

OK,万事大吉,客户在创建CReciver的时候,会根据情况设置不同的method,如果method==2,再设置一个listbox的hwnd就可以了。新的修改又来了,这次状态信息不显示在listbox中,而显示在一个进度条对话框中。如何修改?

没关系,继续method=3,需要的参数继续类似listbox的hwnd一样去set一下就ok了。

显然不是一个好的设计,还记得前几节的描述吗?当时说用ifelse不好,现在看到了不好的结果了。怎么做?有现成的做法。

我想,有耐心看到这里的读者,都是很有修养的,说来说去,不就是想说观察者模式么?嗯,我也觉多太啰嗦了,不说了。

 

原创粉丝点击