第一个是我们之前讲过的方块程序,它的调用关系大致如下:
第二个是一个简单的股市软件的程序,比较长,这里就不贴出代码了,说一下大概的思路:
有一个专门的硬件和配套的驱动、动态链接库。通过动态链接库中的函数,可以将从射频接收到的消息与windows的一个自定义消息绑定,绑定以后,就可以通过消息响应函数OnIRQ来获取消息。通过GetActiveDocument 获得文档类指针,把数据放在文档类中的m_stock中。然后通过GetActiveView获取视类指针,调用视类的Invalidate函数,这个函数引起操作系统发送一个WM_PAINT消息,这个消息会调用视类的OnPaint函数,进而调用OnDraw函数。而OnDraw想要绘图,必须通过GetDocument获取文档类的指针,拿到文档中的数据进行绘图。架构如下:
与第一个程序相比,最大的区别在于这个程序缺少了对数据的保护。数据时public的,并没有通过接口访问。所以程序如果需要改进或者升级,那么需要修改的地方非常多;如果对数据的访问提供了接口,那么只要接口不变,则调用接口的函数就不需要做太大的修改。而且如果提供了接口,那么想要动数据,必须通过接口,这样数据会更加安全。
下面看一个更加复杂的股市软件程序,它使用了多线程技术,路线图画的稍微有点特别,主要强调的是线程间的关系,不是类的关系。
首先,应用程序和框架类组成了整个程序的主线程。在OnCreate中创建了两个线程。一个线程由CUIFrameWnd和CUIThread组成,这类线程成为UI线程,它们有窗口,可以接收消息,我们的数据接收响应函数OnIRQ就放在里面,另一个线程ReadRingThread是一个worker线程,没有窗口,用来读取数据。当每次接收数据以后,调用读环线程来读取数据,储存在另一中数据结构,用来储存和显示当前和历史的数据。读取完毕后再将线程睡眠。
这个程序的奇妙之处在于,虽然OnIRQ和ReadRingThread都操作了数据,但是并没有使用使用线程同步机制来保护数据,这是为什么呢?因为设计者把数据设计成了一个环,读取数据时,只能读取当前写入位置之前的某个位置的数据。所以读写操作始终不会操作同一个内存地址。
这里补充几句关于这个股市软件的数据结构的设计,其实是有大学问的。接收下来的包是一种固定的格式,通过包的类型判断是指数还是某个股票,还是某个信息等等。
但是这些信息中,有的是需要长久储存的,比如每个股票的每一分钟的价格、成交量之类的;有的信息是不需要储存的,比如当前时刻的买一买二买三之类的。而且股市中的股票数量是不断变化的。考虑到这些因素,所设计的数据结构应该是这样:
typedef struct { float New; float Amount; float Volume; CTime Time; }STORAGE;//------------------------------------------------------------实时数据存储数据结构 typedef struct { float LastClose; float Highest; float Open; float New; float Lowest; float Volume; float Amount; float Pbuy1; float Vbuy1; float Pbuy2; float Vbuy2; float Pbuy3; float Vbuy3; float Pbuy4; float Vbuy4; float Psell1; float Vsell1; float Psell2; float Vsell2; float Psell3; float Vsell3; float Psell4; float Vsell4; }REFRESH;//-------------------------------------------实时数据刷新数据结构 class CIndexStockMem/* : public CObject*///-----------构造指数和股票实时数据存储空间 { public: CIndexStockMem(); virtual ~CIndexStockMem(); // void IsCurWindow(CString strCurCode); CArray<STORAGE, STORAGE> m_StorageArray;//--------实时数据存储结构体动态数组 REFRESH m_Refresh;//------------------------------实时数据刷新结构体变量 };
class CIndicator/* : public CObject*///构造所有指数或股票内存的索引 { public: CIndicator(); CIndicator(CIndicator& Indicator); virtual ~CIndicator(); CIndicator& operator = (CIndicator& Indicator); public: CString m_strIndicatorCode;//指数或股票的代码 CString m_strIndicatorName;//指数或股票的名称 CIndexStockMem* m_pIndexStockMem;//指向指数或股票内存的指针 };