图:
 ==========    =========    ========       ========
| CDevice1 |  | CDevice2|  |CDevice3| ... |CDeviceN|
 ==========    =========    ========       ========
      ↑           ↑          ↑            ↑
      ↓           ↓          ↓            ↓
=======================================================
| CWhat ?      |
|      |
=======================================================
...是这样:每一个设备类,可以理解成是操作系统的一个驱动程序!! 
CWhat就是OS,要与driver实现"双向"通信: 即,CWhat要主动调Device, Device在发生中断时也要主动报给CWhat. 
这个比喻与当前情形极其相似,就是底层设备与上层OS间的双向交互设计问题.其实我这么考虑的目的有两个:
1.当某类设备硬件更新时,我能很容易的把CDeviceX替换掉,而不需修正系统的其他模块(即松散藕合)
2.CWhat这个心脏级的控制核心要在松散的基础上保持简洁,不要复杂.
=========================
上个贴子回复,片段:
onestation(新手) 如果不想定义太多接口可这样定义:
struct IDeviceEvent
{
virtual long OnEvent1(int nEventId, void* pSender, void* pParam){};
};
CWhat只用继承IDeviceEvent,再通过nEventId等条件判断。
===============================
ProgrameMan(我要学汇编) :
class IDevice // CWhat 调用设备的抽象借口
class IConnectPoint // 设备调用CWhat 的接口
class CWhat
{
static IConnectPoint* RegisterDevice(IDevice *ptr) // 所有的设备对象调用这个函数向 what 注册自己,同时该函数返回 一个与自己交互的接口。(此类可以实现单件模式)
}
=================================
dick_song(卡菲的伙伴) 
还是觉得定一套通用的设备接口比较好,具体对于不同设备的不同操作就要看DEVICE1 、DEVICE2 等内部的封装了。
===========================
 回复人:fanchka(狼仔) 
to:onestation(新手)
呵呵,这种方法实现的是"单向"交互: 即,device调用CWhat.
当然在OnEvent1中,能返回去掉device1,但仅限制在OnEvent1()中, 当CWhat内部时钟触发轮询,要向device要数据时,这种方法就不足了.这就要在CWhat内部保留一份引用表,时钟一触发就回调各个设备. "双向"就是指这个意思.但我不很情愿用 "引用表",因为回调每个设备的内容不同,
可能device1要调用它的:位置,然后再调用它的速度,最后再取它的状态.而CWhat在调用期间可能要穿插其他的操作来配合device1的返回结果.所以这种情况决定了无法对所有设备抽出一个IDeviceFace这样的接口,无法实现! 
呵呵,也就是说 "引用表" 并非是list<> vector<>之类的东西,而是一个个IDevice1* _dev1; IDevice2* _dev2;...
之类的东西.想像以下,那么多的设备,CWhat中存在那么多的接口指针...,唉,这就是俺"很不情愿"的原因了.
=============================
 回复人:fanchka(狼仔) 
to:ProgrameMan(我要学汇编)
//class IDevice // CWhat 调用设备的抽象借口
//class IConnectPoint // 设备调用CWhat 的接口
呵呵,两个接口包不住啊,要是能包住我就不头痛了.

解决方案 »

  1.   

    不好意思,你可看这段代码,然后说说为什么不适合你
    class IDevice
    {
    public:
    virtual void OnEvent(void*) = 0;
    }class CDevice1
    {
    private:
    void OnInterrupt(void* parameter)
    {
    CKernel::GetInstance()->OnDeviceInterrupt(parameter); }public:
    virtual void OnEvent(void*)
    {
    }}class CDevice2
    {
    public:
    void OnInterrupt(void*)
    { } virtual void OnEvent(void*)
    {
    }
    }class IDeviceInterrupt
    {
    public:
    virtual void OnDeviceInterrupt(void*) = 0;
    virtual void RegisterDevice(IDevice *ptr, IDevice *root) = 0;
    virtual void UnRegisterDevice(IDevice *ptr) = 0;
    }
    class CKernel : public IDeviceInterrupt
    {
    public:
    static IDeviceInterrupt *GetInstance()
    {
    if(0 == pThis) {
    pThis = new CKernel;
    }

    return pThis;
    } public:
    virtual void OnDeviceInterrupt(void *)
    {
    // TODO: add process device's interrupt code at here.
    //       this interface call by CDevice?
    } virtual void RegisterDevice(IDevice *ptr, IDevice *root)
    {
    // TODO: add register a new or replace device code at here.
    //       this interface call by CDevice? if(root != 0) {
    UnRegisterDevice(root);
    } InternalRegister(ptr);
    } virtual void UnRegisterDevice(IDevice *ptr)
    {
    // TODO: add unregister a device code at here.
    //       this interface call by CDevice?
    }
    private:

    void fire_event(void* parameter)
    {
    IDevice *obj;
    for(...)
    {
    obj = ...;
    obj->OnEvent(parameter);
    }
    }

    static IDeviceInterrupt * pThis;
    }
      

  2.   

    可不可以这样, 既然和CWhat打交道的设备是如此多, 差异如此到, 可不可以单独抽象出一个类,如IDevControl, 用来实现对各个设备的不同的控制, 就是说CWhat把对各个Device的控制委托给这个设备控制接口来完成:struct IDevControl
    {
         LRESULT ControlDevice(enum DevTypeId id, CWhat* pWhat, ...//其它参数, 可以非常宽泛);
    };
    这样针对不同的设备, 实现不同的IDevControl接口, 比如:
    class Dev1Controller : public IDevControl{....};
    class Dev2Controller : public IDevControl{....};在CWhat内部维护一个IDevControl的引用表, 处理不同设备时, 根据设备Id, 委托相应的IDevControl完成, 因为ControlDevice(... pWhat...)引用了CWhat, 所以也可以调用CWhat的方法来辅助控制的完成.实现各个Device对CWhat的回调也可类似实现, 比如让CWhat暴露IInterruptHandler接口
    struct IInterruptHandler
    {
        HRESULT ServiceDevice(enum DevTypeId id, ...);
    }这样当CWhat收到不同Device的中断请求后, 委托给相应的Interrupt Handler去处理
      

  3.   

    to:ProgrameMan(我要学汇编)
    呵呵,代码写的很详细.老兄辛苦了. 说说自己的看法,不妥的地方希望多多指教.
    先说单件,在这里的情形中,我对它不支持,也不反对. 
    不支持: 一是它的穿透力太强,在每个device中出现,是不太好的主意.对于你的代码,我更喜欢这种方式:
    class CDevice1:public IDevice //没误解吧? 呵呵
    {
     public:
     CDevice1(IDeviceInterrupt* kernalFace);
     protected:
     IDeviceInterrupt* _kernalFace;
     void OnInterrupt(void* parameter)
    {
      _kernalFace->OnDeviceInterrupt(parameter);
    }
    ...
    }int main()
    {...; IDevice* devFace1 = new CDevice1(CKernel::GetInstance()); ...}
    二者,要费心考虑同步问题. 太多的Device均具备自己的线程, 都回调CWhat的话, 同步..对单件
    来讲不仅在构造析构里要加锁同步,而且在OnDeviceInterrupt(void*)考虑更复杂的同步问题: 不能一把锁锁死,那样并发时10多个线程都在排队等待,这样效率太低,不能这么做啊.
    不反对: 单件是蝎子拉屎--独一份,资源消耗要小,许多调用操作简洁,如果能处理好同步问题,也算不错的选择.再说IDevice的问题. 不能因为咱们称呼一声"设备",就一定就得到一个非常美好的IDevice出来,
    这是理想啊. CWhat想做的事情是非常实际的,有针对性的调用工作,就像OS告诉VGA,给我画个馅饼.
    如果OS对声卡,对内存,对网卡喊"画馅饼"是讲不通的,因为VGA 声卡 内存虽然都是"设备",但从它们
    所实现的功能上看,是不存在共性的. 这就是我一直所说的"这种情况决定了无法对所有设备抽出一个IDeviceFace这样的接口,无法实现!"呵呵,我不是C出身,只是实际工作有点硬件倾向.
      

  4.   

    对于单件或不单件如你所说取决于具体的实现,这个在你的方案中它并不重要。再说IDevice的问题. 不能因为咱们称呼一声"设备",就一定就得到一个非常美好的IDevice出来,
    这是理想啊. CWhat想做的事情是非常实际的,有针对性的调用工作,就像OS告诉VGA,给我画个馅饼.
    如果OS对声卡,对内存,对网卡喊"画馅饼"是讲不通的,因为VGA 声卡 内存虽然都是"设备",但从它们所实现的功能上看,是不存在共性的.我想如果用面向对象的角度去考虑操作系统与设备之间关系的话,我想应该是这样的(当然操作系统是用C 和汇编实现的,在这里只是以面向对象的角度阐述一下我的想法而已)IDevice
    {
     public 
        virtual void in(...) = 0; 
        virtual void out(...) = 0;
        virtual char * get_device_name() = 0;
     }class ISoundDevice
    {
    public:
       virtual void play(...) = 0;
       virtual void setVolume(...) = 0;
       ...
    }class IVideoDevice
    {
    public:
        virtual draw(...) = 0;
        virtual set_brightness(...) = 0;
        ....
    }
      

  5.   

    错上面的 ISoundDevice 和 IVideoDevice 是需要派生自 IDevice 的
      

  6.   

    要费心考虑同步问题. 太多的Device均具备自己的线程, 都回调CWhat的话, 同步..对单件
    来讲不仅在构造析构里要加锁同步,而且在OnDeviceInterrupt(void*)考虑更复杂的同步问题: 不能一把锁锁死,那样并发时10多个线程都在排队等待,这样效率太低,不能这么做啊.**************************************************************
    呵呵,我也是啊,说错了也请大家指正如果你只使用在系统中只存在一个 CWhat 的话,无论你怎么做都会面临同步的问题,
    如果每个设备都是用一个独立的 CWhat 的话就不存在你说的问题了,把 IDeviceX 派生自 CWhat 不就行了吗?
      

  7.   

    to:crumpy()
    我对你的意思理解的不知道正不正确,是不是这样:
     ==========    =========    ========       ========
    | CDevice1 |  | CDevice2|  |CDevice3| ... |CDeviceN|
     ==========    =========    ========       ========
          ↑           ↑          ↑            ↑
          ↓           ↓          ↓            ↓
      Dev1Ctrler Dev2Ctrler   ...            ...
          ↑           ↑          ↑            ↑
          ↓           ↓          ↓            ↓
    =======================================================
    | CWhat ?      |
    ======================================================
    要是如此, CDevice1 回调 CWhat时, 后者可以委托Dev1Ctrler做善后工作,但,Dev1Ctrler还是
    要把处理完的相关状态 写入 CWhat里面, 如果直接调pWhat写, 那么这样Dev1Ctrler Dev2Ctrler ...
    与CWhat还是紧藕合关系; 如果间接调个接口..比如 IRecvState (class CWhat:public IRecvStates)
    嗯...能行的通;  CWhat 回调 CDevice1的时候,是由 Dev1Ctrer 完成操作,并将处理结果回写入
    CWhat里面.
    呵呵,感觉CWhat调用CDevice1,CDevice2...时简单了,但整个架构膨胀太大了,考虑考虑...
      

  8.   

    to:ProgrameMan(我要学汇编) 
    呵呵,老兄够逗的. 
    对于抽象层次来讲,我的观点是够用就好,不能过,过犹不及.
    抽象的本质,就是共性嘛. 只要够成足够的共性,就可以抽成一层. 
    但对于抽象往往有个误区,不考虑系统实际运行情况,但凭意识去做抽象工作,
    这样抽的层次往往不是高了,就的低了.呵呵,我也是这么过来的,做系统往往需要权衡,怎么着让它松散,怎么让它简洁,怎么让它性能高效,
    三者之间如何平衡才对系统真正最优,在些关键系统里,是常常要仔细考虑的.
      

  9.   

    to fanchka(狼仔) 呵呵,你要是这么说,我就无话可说了,看来你走过的那关我还没过呢,学习去了
      

  10.   

    to:fanchka(狼仔) ==========    =========    ========       ========
    | CDevice1 |  | CDevice2|  |CDevice3| ... |CDeviceN|
     ==========    =========    ========       ========
          ↑           ↑          ↑            ↑
          ↓           ↓          ↓            ↓
      Dev1Ctrler Dev2Ctrler   ...            ...
          ↑           ↑          ↑            ↑
          ↓           ↓          ↓            ↓
    =======================================================
    | CWhat ?      |
    ======================================================你画的图就是我想的, 我不过是借鉴了硬件系统中的组织结构, 在一个硬件系统中, CPU(相当于你这里的CWhat), 并不会直接操作外设的工作, 而是通过与外设相连的硬件控制器来完成的, 例如CPU不会直接控制硬盘(相当你这里的一个Device)马达的旋转, 不会控制磁头的运动, 这些是用硬盘控制器(相当于图中DevXCtrler)完成的, CPU所做的只是象不同设备的控制器"暴露"出来的端口(相当于这里的接口)写入正确的控制字, 或从中读出状态字, 所以在这种程度上, "端口"的概念进一步降低了CPU和硬件控制器的偶合.所以你说CWhat和DevXCtrler是紧偶合, 是对的, 因为这就好比CPU为了控制软盘, 要有专用软盘指令集, 为了控制硬盘又要有一套专用的硬盘指令集, 但实际上CPU并没有这些指令集, 也不可能有. 所以为了降低CWhat和DevXCtrler的偶合, 可以再模拟出端口的概念, 比如IPort.这样确实会造成代码膨胀, 但设计模式好像就是这样的, 如果通过一定代码的膨胀可以实现各个单元模块的高内聚, 低偶合, 易于维护和扩展, 那这种膨胀还是值得的. 这只是我的拙见
      

  11.   

    没太明白,汗~~如果单纯的类似OS与device打交道,那就以OS与Device的交互为原型创建不就得了,比如Windows的消息驱动,DOS下的中断机制,接口指针么,注册表里存了一堆堆的,设备对象也不用从一个Object下继承出来,太累了,还涉及兼容性问题,如果都是自己的东西那倒也无妨,一个device跟多个device没什么区别,CWhat负责交互处理,Device之间互不干涉
      

  12.   

    这个好象没LZ想的那么复杂吧。我写过类似的处理程序,在我的程序里,我把楼主说的CWhat用一个主线程来实现,然后定义一个全局的结构,这个结构主要用于描述哪个设备发生了哪个事件,当一个设备发生某个事件的时候,设备自动填充结构,然后触发一个信号,主线程一直在WAIT。当信号被主线程捕获的时候,主线程就通过设备预先定义的处理模式处理(处理模式可以外扩的,只要在写设备对象的时候有统一的入口),其实主线程更多的是完成任务调度工作,和共享资源的管理。每个设备应该是完全独立的部分,通过主线程的调度互相作用而已。
      

  13.   

    to:crumpy() 
    呵呵,我正在考虑适当的变通一下,device和CWhat间用 接口 和 DevXCtrler结合的方式, 复杂的设备用DevXCtroler处理,简单的就用接口.to:loseme915(郁闷) 
    呵呵,很不错的想法,有几个疑问点: 全局结构里面的存类的成员函数地址吗? 那样来实现CWhat回调Device以获取它的状态,呵呵是这个意思吧?  
    呵呵,遗憾的是,要回调很多个函数啊,而且参数列表也不同,这又回到"无法定义一个统一接口"问题上了.  设备发信号是个好的想法,但如果CWhat没来得及处理,而它又发了第二个信号,这个设备的前后两个状态有覆盖的情形,如果用容器存储每个状态的话,唉,要很大的开销啊.系统内核级的信号切换也很耗资源啊.  CWhat主动调用Device,也不好实现啊.  
    呵呵,不过刚开始看的时候,确实有些心动.
      

  14.   

    to fanchka(狼仔)我的想法确实太复杂不实际, 没看到前面你需要越简洁, 够用就好的要求,可能会走上不归路, 请考虑其他人的想法吧.
      

  15.   

    to: aceouter(outer) 
    简单问题,要简单处理. 复杂问题也要努力简单处理,但简单始终是相对的.设计基本出来了,有时间就把代码贴上来.
      

  16.   

    我的搜索引擎继7月份第二次开放测试后,现在进行第三次开放测试,请大家多批评指正。
    无为搜索引擎开始测试了啊
    (第三次开放测试啊)  网站运行后的效果请参见: 
    http://219.233.38.213/Search/WuWei.aspx
    代码下载地址 :http://www.ofile.cn/se.rar  
    前两次用 CGI+html做了一个版本还做了一个 Java版本 (这次比较忙,所以没来的及更新,有需要的跟我说一声啊)
    这次用 C# ASP.NET做一个 玩玩
    做的不好,请大家做指教啊!
    这次还实现了计算器功能啊
    可以搜索一下 sin(1+2) 看看 
    另外 sh=1&sf=1&ua=1&sa=1&st=1&wh=1&ph=1     把这里面的 1改成 0看看 每个部分都是可以控制的:)
    至于为什么要把客户端网站部分开源,请参考我第二次开放测试时即兴写的文章 :   最近我研究搜索引擎 七 (长长中国人的志气篇)  
    http://www.baidu.com/s?wd=%D7%EE%BD%FC%CE%D2%D1%D0%BE%BF%CB%D1%CB%F7%D2%FD%C7%E6%C6%DF&cl=3
    承盟广大网友看的起 该文目前已经被全国 2000多网站转载
    先说一下技术指标: 本次我收录了全国30多万个一、二级域名, 网页采集用了三周时间,目前已经采集了 12万网站,平均每个网站收录 30篇网页,索引进行了 10天,
    目前已经索引了 100多万网页 。 搜索核心部分采用这次采用 C  语言开发。 客户端提供三个版本一个CGI+HTML版本,一个 Java版本,一个 ASP.NET版本。代码下载地址 :http://www.ofile.cn/se.rar 
    搜索网站客户端(.NET版本)整体解决方案(源代码)
    (第三次开放测试啊)  网站运行后的效果请参见: 
    http://219.233.38.213/Search/WuWei.aspx
      

  17.   

    实际上我比较同一这个的观点onestation(新手)如果不想定义太多接口可这样定义:
    struct IDeviceEvent
    {
    virtual long OnEvent1(int nEventId, void* pSender, void* pParam){};
    };
    CWhat只用继承IDeviceEvent,再通过nEventId等条件判断。呵呵,抽象过头了工作量太大,不要和自己过不去啊,你看看 Office 的插件实现方式,应该会有不少收获
      

  18.   

    汗颜,项目赶工期,没时间整理贴上来.
    下面的代码没有整理,大体能说明一下思路.
    interface IStateSender
    {
    virtual BOOL InitSender() = 0;  
    virtual void OnReportState() = 0;
    virtual void OnReportStateWithMsg(DeviceMsg devMsg, /*OUT*/ void* data) = 0;
    virtual void RegStateCollector(IStatesCollector* pCollector) =0;
    virtual int  ToDoWork(DeviceMsg devMsg, /*IN*/ void* data) = 0;
    virtual DevType SenderType() = 0;
    };interface IStatesCollector
    {
    virtual int OnRecvedDevMsg(DeviceMsg devMsg, void* pParam) = 0;
    virtual void RegStateSender(IStateSender* pSender) = 0;
    virtual void RegStatesCollector(IStatesCollector* pCollecter) = 0;
    };class CDispatchCenter : public IStatesCollector //it's CWhat.
    {
    public:
    static CDispatchCenter* Instance();
    static void Release(); virtual int  OnRecvedDevMsg(DeviceMsg devMsg, void* pParam) ;
    virtual void RegStateSender(IStateSender* pSender);
    virtual void RegStatesCollector(IStatesCollector* pCollector);protected:
    CDispatchCenter();
    virtual ~CDispatchCenter(); void OnSocketMsg(_tagCommuCmd* pParam);
    void DispatchMsg(DeviceMsg devMsg, void* pParam);protected:
    static CDispatchCenter* _instance;
    IStateSender*  _arrSender[Dev_Count];
    list<IStatesCollector*> _lstCollector;
    CRealTimeStates* _rts; typedef list<IStateSender*>::iterator SenderIterator;
    typedef list<IStatesCollector*>::iterator CollectorIterator;
    };enum MotorDeviceMsg
    {
    MDMsg_Nothing  =-1,
    MDMsg_AutoRun =0,
    MDMsg_ManuRun,
    MDMsg_Stop,
    MDMsg_ArrivedStation,
    MDMsg_IncSpeed,
    MDMsg_DecSpeed,
    MDMsg_TurningByAngle,
    MDMsg_Count,
    };typedef struct _tagMotor_A2D_Msg //A2D: application to device
    {
    MotorDeviceMsg msg;
    BOOL isStation;
    MovedControl mc;
    MotorStatues ms[M_Count];
    } MotorMsgArg;class CMotorProxy : public IStateSender
    {
    public:
    CMotorProxy();
    virtual ~CMotorProxy(); virtual BOOL InitSender() ;
    virtual void OnReportState();
    virtual void OnReportStateWithMsg(DeviceMsg devMsg, void* data);
    virtual void RegStateCollector(IStatesCollector* pCollector) ;
    virtual int  ToDoWork(DeviceMsg devMsg, /*IN*/ void* data) ;
    virtual DevType SenderType();protected:
    CMotor* _motor;
    IStatesCollector* _collecter;};class CCmdProxy : public IStatesCollector
    {
    public:
    static CCmdProxy*  Instance();
    static void  Release(); void OnSocketMsg(INT nCmd, const char* data, int len);
    virtual int OnRecvedDevMsg(DeviceMsg devMsg, void* pParam);
    virtual void RegStateSender(IStateSender* pSender){}
    virtual void RegStatesCollector(IStatesCollector* pCollecter);protected:
    CCmdProxy();
    virtual ~CCmdProxy(); void AutoRunTask(const char* data, int len);
    void ManualRunTask(const char* data, int len);
    void PantiltTask(const char* data, int len);
    void IOCtrlTask(const char* data, int len);
    void CameraTask(const char* data, int len);
    void AudioTask(const char* data, int len);
    protected:
    static CCmdProxy* _instance;
    IStatesCollector* _dispCenter;
    };