2009-02-13 89 views
4

我有一个有趣的问题。考虑这个类层次:具体类具体方法

class Base 
{ 
public: 
    virtual float GetMember(void) const =0; 
    virtual void SetMember(float p) =0; 
}; 

class ConcreteFoo : public Base 
{ 
public: 
    ConcreteFoo("foo specific stuff here"); 

    virtual float GetMember(void) const; 
    virtual void SetMember(float p); 

    // the problem 
    void foo_specific_method("arbitrary parameters"); 
}; 

Base* DynamicFactory::NewBase(std::string drawable_name); 

// it would be used like this 
Base* foo = dynamic_factory.NewBase("foo"); 

我已经离开了DynamicFactory定义以及如何构建器 其注册。 Builder对象与名称 相关联,并将分配Base的具体实现。实际的 实现与shared_ptr处理内存 回收有点复杂,但它们对我的问题并不重要。

ConcreteFoo具有类特定的方法。但是由于具体实例 是在动态工厂中创建的,因此具体类不可知或者可以访问 ,它们只能在源文件中声明。我如何 foo_specific_method暴露给Base*的用户?

我添加了我提出的解决方案作为答案。我已将它们命名为 ,因此您可以在答案中轻松引用它们。

我不只是寻找我原来的解决方案的意见,新的 将不胜感激。

+0

请让你的答案社区维基答案!谢谢。 – strager 2009-02-13 21:54:56

+0

或者,完全删除你的答案。虽然我猜这可能会起作用... – strager 2009-02-13 21:55:41

回答

2

演员会比大多数其他解决方案快,但是:

在基类中添加:

void passthru(const string &concreteClassName, const string &functionname, vector<string*> args) 
{ 
    if(concreteClassName == className) 
     runPassThru(functionname, args); 
} 

private: 
    string className; 
    map<string, int> funcmap; 
    virtual void runPassThru(const string &functionname, vector<string*> args) {} 

在每个派生类:

void runPassThru(const string &functionname, vector<string*> args) 
{ 
    switch(funcmap.get(functionname)) 
    { 
     case 1: 
      //verify args 
      // call function 
     break; 
     // etc.. 
    } 
} 

// call in constructor 
void registerFunctions() 
{ 
     funcmap.put("functionName", id); 
     //etc. 
} 
0

将特殊功能添加到Base

最简单和最不可接受的解决方案是将 foo_specific_method添加到Base。然后,不使用 的类可以将其定义为空。这不起作用,因为 用户被允许使用 dynamic_factory注册他们自己的建造者。新班级也可能有具体的班级 具体方法。

本着这种解决方案的精神,稍微好一点。将通用 函数添加到Base

class Base 
{ 
    ... 
    /// \return true if 'kind' supported 
    virtual bool concrete_specific(int kind, "foo specific parameters"); 
}; 

这里的问题是 concrete_specific对不同参数集的有可能相当多的过载。

0

只需施放它。

当需要特定的方法时,通常您知道 Base*实际上是ConcreteFoo。因此,只要确保类的定义 ConcreteFoo为方便和:

ConcreteFoo* foo2 = dynamic_cast<ConcreteFoo*>(foo); 

一个我不喜欢这种解决方案的原因是dynamic_casts很慢, 需要RTTI。

下一步是避免dynamic_cast。

ConcreteFoo* foo_cast(Base* d) 
{ 
    if(d->id() == the_foo_id) 
    { 
     return static_cast<ConcreteFoo*>(d); 
    } 

    throw std::runtime_error("you're screwed"); 
} 

这就需要在基类多一个方法,其是完全可接受的 ,但它需要的ID进行管理。当用户可以在动态工厂中注册他们自己的建造者时,难以获得 。

我不太喜欢任何铸造解决方案,因为它需要在使用专门方法的地方定义 用户类。 但也许我只是一个范围纳粹。

1

CrazyMetaType解决方案。

该解决方案没有经过深思熟虑。我希望有人可能 有类似的经验。我看到这适用于 已知类型的未知数的问题。这非常漂亮。我 想将它应用到未知类型的未知数量的***秒***

的基本思想是CrazyMetaType收集参数是类型 安全的方式,然后执行具体的具体方法。

class Base 
{ 
    ... 
    virtual CrazyMetaType concrete_specific(int kind) =0; 
}; 

// used like this 
foo->concrete_specific(foo_method_id) << "foo specific" << foo_specific; 

我的一个担心这个解决方案是CrazyMetaType将是 出奇的复杂,得到这个工作。我愿意接受这个任务,但我不能指望将来的用户成为C++专家,只需添加 一个具体的具体方法即可。

0

cstdarg解决方案。

Bjarn Stroustrup的说:

良好定义的程序需要的量, 参数类型不完全指定至多几个函数。重载函数和使用默认参数 功能时,可以使用一个原本考虑离开 参数类型未指定的照顾类型 在大多数情况下检查。只有当双方的论点 参数的类型而异数,是必要的省略号

class Base 
{ 
    ... 
    /// \return true if 'kind' supported 
    virtual bool concrete_specific(int kind, ...) =0; 
}; 

这里的缺点是:

  • 几乎没有人知道如何使用cstdarg正确
  • 它不会觉得非常C++ - y
  • 它不是类型安全的。
0

您可以创建Base的其他非具体子类,然后在DynamicFactory中使用多个工厂方法吗?

你的目标似乎是颠覆子类化的观点。我很想知道你在做什么需要这种方法。

0

如果具体对象具有类特定的方法,那么它意味着你只可以明确调用该方法时,你正在处理的类的实例,而不是当你处理与通用基础类。这是关于你正在运行一个switch语句的b/c检查对象类型吗?

我从不同的角度接近这个,使用“不可接受的”第一解决方案,但不带参数,与具有将存储它的状态成员变量的具体对象。虽然我猜这会迫使你有一个成员关联数组作为基类的一部分,以避免强制设置状态。

您也可能想尝试一下Decorator模式。

0

你可以做一些类似的CrazyMetaType或cstdarg说法,但简单和C++ - ISH。 (也许这可能是SaneMetaType。)只需为参数concrete_specific定义一个基类,并使人们从中派生出特定的参数类型。像

class ConcreteSpecificArgumentBase; 

class Base 
{ 
    ... 
    virtual void concrete_specific(ConcreteSpecificArgumentBase &argument) =0; 
}; 

当然了什么,你会需要RTTI来出来的东西concrete_specific每个版本中进行排序。但如果ConcreteSpecificArgumentBase设计得很好,至少它会让concrete_specific相当简单。

0

怪异的一部分是, DynamicFactory的用户会收到一个Base类型,但是当它是ConcreteFoo时需要做特定的事情。

也许不应该使用工厂。

尝试一下像创建ConcreteFoo自己其他的依赖注入机制,通过一个ConcreteFoo类型的指针那些谁需要它,一个基本类型指针等。

0

的情况下,似乎假定用户将与您ConcreteType合作,并知道它是这样做的。

在这种情况下,似乎你可以在你的工厂另一种方法,返回ConcreteType *,如果客户知道他们正在处理的具体类型和需要抽象的那个水平的工作。