在没有RTTI的情况下使用中央管理器管理不同类 [英] Managing diverse classes with a central manager without RTTI

查看:191
本文介绍了在没有RTTI的情况下使用中央管理器管理不同类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个设计问题,已经bug了我一段时间,但我找不到一个好的(在OOP意义上)这个解决方案。语言是C ++,我会继续回到RTTI - 这通常被称为坏设计的指标。



假设我们有一组不同类型的模块作为不同的类。每种类型的模块的特征在于定义的接口,然而实现可以变化。
因此我的第一个想法是为每种模块(例如IModuleFoo,IModuleBar等)创建一个接口(纯抽象)类,并在独立类中实现。到目前为止这么好。

  class IModuleFoo {
public:
virtual void doFoo()
};

class IModuleBar {
public:
virtual void doBar()= 0;
};

另一方面,我们有一组(应用程序)类,那些模块,但只通过接口 - 即使模块本身可能使用其他模块。但是,所有应用程序类将共享相同的模块池。我的想法是为所有模块创建一个管理器类(ModuleManager),应用程序类可以查询他们需要的模块类型。可用的模块(和具体实现)在管理器初始化期间设置,并且可能随时间而变化(但这不是我的问题的真正部分)。



不同模块类型的数量最可能> 10,并且可能随时间增加,似乎不适合于单独存储对它们的引用(或指针)。此外,管理器可能需要在所有托管模块上调用几个功能。因此,我创建了另一个接口(IManagedModule),我现在可以使用IManagedModules的容器(列表,集,任何)将它们存储在管理器中。

  class IManagedModule {
public:
virtual void connect()= 0;
{...}
};

结果是,需要管理的模块需要继承来自IManagedModule和适当接口类型。



但是当我想到ModuleManager时,事情变得丑陋。可以假设在每个时间处存在每个模块类型的至多一个实例。因此,如果可以做这样的事情(其中manager是ModuleManager的实例),一切都会很好:

  IModuleFoo * pFoo = manager.get(IModuleFoo); 

但我确定不是。我也想过一个基于模板的解决方案,如:

  IModuleFoo * pFoo = manager.get< IModuleFoo> 

这可以工作,但我不知道如何找到正确的模块是一套IManagedModules - 那就是没有使用RTTI,当然。



一种方法是向IManagedModule提供虚拟的 getId()方法,依赖于每个类型使用非歧义的id的模块,并做自己的指针铸造。但是这只是重新发明轮(即RTTI),并且需要在实现类(提供正确的ids等...)内的很多纪律,这是不可取的。



长故事短 - 问题是如果真的没有办法绕过某种RTTI在这里,在这种情况下RTTI甚至可能是一个有效的解决方案,或者如果可能有一个更好的(更清洁,更安全,...)设计,相同的灵活性(例如应用程序类和模块类之间的松耦合...)?我错过了什么吗?

解决方案

听起来你正在寻找类似COM的 QueryInterface 。现在,你不需要完全实现COM,但基本原则是:你有一个基类,有一个虚函数,你传递一个标识符,指定你想要的接口。然后,虚拟函数查看是否可以实现该接口,如果是,则传回指向该接口的指针。



例如:



struct IModuleBase {
//更改名称,以免后来的程序员与真正的COM混淆
virtual bool LookupInterface(int InterfaceID, void ** interfacePtr)= 0;

//简单的模板包装器
template< typename Interface>
Interface * LookupInterface(){
void * ptr;
if(!LookupInterface(Interface :: INTERFACE_ID,& ptr))return NULL;
return(Interface *)ptr;
}
};

struct IModuleFoo:public IModuleBase {
enum {INTERFACE_ID = 42};
virtual void foo()= 0;
};

struct SomeModule:public IModuleFoo {
virtual bool LookupInterface(int interface_id,void ** pPtr){
switch(interface_id){
case IModuleFoo :: INTERFACE_ID:
* pPtr =(void *)static_cast< IModuleFoo *>(this);
return true;
默认值:
return false;
}
}

virtual void foo(){/ * ... * /}
};

这是一个有点笨重,但它不是太糟糕,没有RTTI你没有太多除了这种方法外,还有一个选择。


I have a design question that has been bugging me for a while but I cannot find a good (in a OOP sense) solution for this. The language is C++ and I keep coming back to RTTI - which is often referred to as an indicator for bad design.

Suppose we have a set of different kinds of modules implemented as different classes. Each kind of module is characterized by a defined interface, however the implementation may vary. Thus my first idea was to create an interface (pure abstract) class for each kind of module (e.g. IModuleFoo, IModuleBar etc.) and the implementations in seperate classes. So far so good.

class IModuleFoo {
  public:
    virtual void doFoo() = 0;
};

class IModuleBar {
  public:
    virtual void doBar() = 0;
};

On the other hand we have a set of (application) classes and each of them uses a couple of those modules but only through the interfaces - even the modules themselves might use other modules. However, all of the application classes will share the same pool of modules. My idea was to create a manager class (ModuleManager) for all modules which application classes can query for the module types they need. The available modules (and the concrete implementation) are set up during initialization of the manager and may vary over time (but that is not really part of my question).

Since the number of different module kinds is most probably >10 and may increase over time it does not appear suitable to me to store references (or pointers) to them separately. In addition there might be a couple of functions the manager needs to invoke on all managed modules. Thus I created another interface (IManagedModule) with the benefit that I can now use a container (list, set, whatsoever) of IManagedModules to store them in the manager.

class IManagedModule {
  public:
    virtual void connect() = 0;
    { ... }
};

The consequence is that a module that shall be managed needs to inherit both from the IManagedModule and from the appropriate interface for its type.

But things turn ugly when I think about the ModuleManager. It can be assumed that there is at most one instance of each module type present at each time. Thus if it was possible to do something like this (where manager is the instance of the ModuleManager), everything would be fine:

IModuleFoo* pFoo = manager.get(IModuleFoo);

But I'm pretty sure that it's not. I also thought about a template based solution like:

IModuleFoo* pFoo = manager.get<IModuleFoo>();

That could work but I have no idea how to find the right module within the manager if all I have is a set of IManagedModules - that is without the use of RTTI, of course.

One approach would be to provide IManagedModule with a virtual getId() method, rely on the implementations to use non-ambigous ids for each kind of module and do the pointer casting on your own. But that's just reinventing the wheel (namely RTTI) and requires a lot of discipline within the implementing classes (providing the right ids etc...) which is not desirable.

Long story short - the question is if there is really no way around some kind of RTTI here and in this case RTTI might even be a valid solution or if there might be a better (cleaner, safer, ...) design which exhibits the same flexibility (e.g. loose coupling between application classes and module classes...)? Did I miss anything?

解决方案

It sounds like you're looking for something similar to COM's QueryInterface. Now, you don't need to implement COM entirely, but the basic principle stands: You have a base class, with a virtual function, to which you pass an identifier specifying which interface you want. The virtual function then looks to see if it can implement that interface, and if so, passes back a pointer to that interface.

For example:

struct IModuleBase {
    // names changed so as not to confuse later programmers with true COM
    virtual bool LookupInterface(int InterfaceID, void **interfacePtr) = 0;

    // Easy template wrapper
    template<typename Interface>
    Interface *LookupInterface() {
        void *ptr;
        if (!LookupInterface(Interface::INTERFACE_ID, &ptr)) return NULL;
        return (Interface *)ptr;
    }
};

struct IModuleFoo : public IModuleBase {
    enum { INTERFACE_ID = 42 };
    virtual void foo() = 0;
};

struct SomeModule : public IModuleFoo {
    virtual bool LookupInterface(int interface_id, void **pPtr) {
        switch (interface_id) {
            case IModuleFoo::INTERFACE_ID:
                *pPtr = (void*)static_cast<IModuleFoo *>(this);
                return true;
            default:
                return false;
        }
    }

    virtual void foo() { /* ... */ }
};

It's a bit unwieldy, but it's not too bad, and without RTTI you don't have much of a choice besides an approach like this.

这篇关于在没有RTTI的情况下使用中央管理器管理不同类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆